Extract duplicate values from a dictionary - python

I am trying to find a way of deleting duplicate shaders in Maya using Python Dictionaries.
Here is what I'm doing:
I want to put all maya shaders into a dictionary as keys and put the corresponding texture file as the value. Then I want the script to run through the dictionary and find any keys that share the same value and stuff them into an array or another dictionary.
This is basically what I have right now:
shaders_dict = {'a': somePath, 'b': somePath,
'c': differentPath, 'd': differentPath}
duplicate_shaders_dict = {}`
how can I now run through that dictionary to compile another dictionary that looks something like this:
duplicate_shaders_dict = {'b':somePath, 'd':differentPath }
And the tricky part being since there are duplicates I want the script to skip the original key so it doesn't also get stuffed in to duplicate shaders dictionary.

I would probably do something like this. First, make the inverse dictionary:
>>> from collections import defaultdict
>>>
>>> shaders_dict = {'a':'somePath', 'b':'somePath', 'c':'differentPath', 'd':'differentPath'}
>>>
>>> inverse_dict = defaultdict(list)
>>> for k,v in shaders_dict.iteritems():
... inverse_dict[v].append(k)
...
>>> inverse_dict
defaultdict(<type 'list'>, {'differentPath': ['c', 'd'], 'somePath': ['a', 'b']})
This basically inverts the dictionary by looping over every key, value pair and appending the key to a list associated with the value.
Then split this:
>>> first_shaders_dict = {}
>>> duplicate_shaders_dict = {}
>>> for v, ks in inverse_dict.iteritems():
... first, rest = ks[0], ks[1:]
... first_shaders_dict[first] = v
... for r in rest:
... duplicate_shaders_dict[r] = v
...
>>> first_shaders_dict
{'a': 'somePath', 'c': 'differentPath'}
>>> duplicate_shaders_dict
{'b': 'somePath', 'd': 'differentPath'}
Hmm. This assumes that the texture files are hashable and so can serve as dictionary keys. If they're not, then I'd have to work around that. Also, since as #freespace notes there's no ordering here, if you wanted a particular order we'd have to iterate over sorted keys or the like.
--
Update: I didn't like the above much. Shorter itertools-based version:
>>> import itertools
>>> shaders_dict = {'a':'somePath', 'b':'somePath', 'c':'differentPath', 'd':'differentPath'}
>>> keys = sorted(sorted(shaders_dict),key=shaders_dict.get)
>>> by_val = [(v, list(ks)) for v, ks in itertools.groupby(keys, shaders_dict.get)]
>>> first_dict = dict((ks[0],v) for v,ks in by_val)
>>> duplicate_dict = dict((k,v) for v,ks in by_val for k in ks[1:])
>>> first_dict
{'a': 'somePath', 'c': 'differentPath'}
>>> duplicate_dict
{'b': 'somePath', 'd': 'differentPath'}

One simple solution is to reverse the dictionary. Given:
>>> d = {'a': 'somePath', 'b': 'somePath',
... 'c': 'differentPath', 'd': 'differentPath'}
You can reverse it like this:
>>> r = dict((v,k) for k,v in d.iteritems())
Which gives you:
>>> r
{'differentPath': 'd', 'somePath': 'b'}
And if you reverse that, you have the original dictionary with duplicates removed:
>>> d = dict((v,k) for k,v in r.iteritems())
>>> d
{'b': 'somePath', 'd': 'differentPath'}

Related

Having trouble defining a function that will make creating a dictionary easier

I am trying to create a player_def function that will make creating a dictionary a little easier.
Looking at it now, this is probably kind of dumb because I can just do players["betts"]["avg"]=340, right? Anyway, to understand how Python works I would be grateful if any of you can explain why the following code is returning a key error instead of creating a nested dictionary.
def player_def(x,y,z):
players[x][y]=z
player_def("betts","avg",340)
print(players["betts"])
The easiest solution would be to use a collections.defaultdict:
from collections import defaultdict
players = defaultdict(dict)
def player_def(x,y,z):
players[x][y] = z
player_def("betts","avg",340)
print(players["betts"])
# {'avg': 340}
We define players as a defaultdict of dict. When we do:
players["betts"]["avg"] = 340
if players doesn't yet have a betts key, a new one is created on the fly with an empty dict as value. So, we can add "avg": 340 to this new dict.
Do you mean this? I'm sorry, but my query does not respond to your problem in a comment, so I had to put it as a possible solution / explanation.
>>> d={}
>>> d
{}
>>> d['a'] = {'b' : {'c','d','e'} }
>>> d
{'a': {'b': {'c', 'e', 'd'}}}
>>>
>>> d['a']['b']
{'c', 'e', 'd'}
///EDIT: So when the dictionary already exists, then you can change its contents. However, if you want to add a new pair (to the right side of an existing key), you must add to the existing key, a non-existent, just above syntax. I guess I explain that complicated, sorry.
>>> d['a']['b'] = "4"
>>> d
{'a': {'b': '4'}}
>>> d['a']['b'] = ["4","test","hello"]
>>> d
{'a': {'b': ['4', 'test', 'hello']}}
>>> d['a']['b'] = (1,2,3,4)
>>> d
{'a': {'b': (1, 2, 3, 4)}}
>>>
Another example from Python console:
>>> test = {}
>>> test['betts']['avg'] = 300
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
KeyError: 'betts'
>>> test['betts'] = {}
>>> test['betts']['avg'] = 300
>>> test
{'betts': {'avg': 300}}
>>>

Restructuring Nested Dictionaries Python

Let's say I have the following two dictionaries:
D1={'a':['a2','a3'], 'b':['b5','b7'], 'c':['c4']}
D2={'a':2, 'b':2, 'c':5}
I would like to restructure them so that the values of D2 are the keys that nest the items of D1. Basically I would like the following dictionary:
DictIWant={2:{'a':['a2','a3'], 'b':['b5','b7']}, 5:{'c':['c4']}}
What is the best and most efficient way of doing this? I've stumbled around a bit and have a very clunky way of getting what I want, but I figured it'd be good to learn the correct way of doing this.
So far my first step has been to use reverse D2 as follows:
>>> from collections import defaultdict
>>> D2_Inv = defaultdict(list)
>>> for k, v in D2.items():
... D2_Inv[v].append(k)
...
>>> D2_Inv=dict(D2_Inv)
>>> D2_Inv
{2: ['a', 'b'], 5: ['c']}
I'm not that familiar with default dict, but I found out how to do that from this question: How to reverse a dictionary that it has repeated values (python).
Then, I've been doing the following:
>>> NextStep=defaultdict(list)
>>> for Key,Value in D2_Inv.items():
... for SubValue in Value:
... New= dict({SubValue: D1[SubValue]})
... NextStep[Key].append(New)
...
>>> NextStep=dict(NextStep)
>>> print NextStep
{2: [{'a': ['a2', 'a3']}, {'b': ['b5', 'b7']}], 5: [{'c': ['c4']}]}
Perhaps because I'm not familiar with defaultdict, I'm not sure how to get a dictionary of dictionaries rather than a dictionary of lists of dictionaries.
So then I've been taking this last step to fix that:
>>> DictIWant={}
>>> for k,v in NextStep.items():
... F=v[0].copy()
... for i in v[1:]:
... F.update(i)
... DictIWant[k]=F
...
>>> print DictIWant
{2: {'a': ['a2', 'a3'], 'b': ['b5', 'b7']}, 5: {'c': ['c4']}}
I know this is a very clunky way of getting the result I'm looking for. Can someone please offer a more efficient / correct solution?
You can use a collections.defaultdict(dict) to build this pretty simply.
from collections import defaultdict
D1={'a':['a2','a3'], 'b':['b5','b7'], 'c':['c4']}
D2={'a':2, 'b':2, 'c':5}
D3 = defaultdict(dict)
for k,v in D2.items():
D3[v].update({k: D1[k]})
print(D3)
# defaultdict(<class 'dict'>, {2: {'b': ['b5', 'b7'], 'a': ['a2', 'a3']}, 5: {'c': ['c4']}})
Since every element in your sub-dicts is key: D1[key] (w.r.t. D2's key,value pairs), you use a defaultdict(dict) to reference a possibly new dictionary at D3[value], then update it with the new element.
You can also use dict.setdefault if you'd rather avoid the defaultdict.
...
D3 = {}
for k,v in D2.items():
D3.setdefault(v, {}).update({k: D1[k]}) # equivalent

Save a dictionary key as a variable

I'm working on a small framework and I've found a place where it would be beneficial to save a dictionary key as variable.
The problem I have is that the dictionary may have any number of layers, so it's not just a case of storing the final key. For example in the below I am accessing ['dig']['result'], but that could equally be ['output'] or ['some']['thing']['strange']
if result:
if self.cli_args.json:
pprint(result)
else:
print result['dig']['result']
I could save the key as a string and use eval() in something such as:
key="['test']"
test_dict = { "test" : "This works" }
eval("test_dict" + key)
>>> 'This works'
But eval is really dirty right? :-)
Is there a nice / pythonic way to do this?
To handle an arbitrary depth of key nesting, you can iterate over a sequence (e.g. tuple) of the keys:
>>> d = {'a': {'b': {'c': 'd'}}}
>>> d['a']['b']['c']
'd'
>>> keys = ('a', 'b', 'c') # or just 'abc' for this trivial example
>>> content = d
>>> for k in keys:
content = content[k]
>>> content
'd'
>>> def access(o,path):
... for k in path.split('/'):
... o = o[k]
... return o
...
>>> access({'a': {'b': {'c': 'd'}}},'a/b/c')
'd'

How can you get a python dictionary to have duplicate keys holding values?

I'm working on an assignment. Is there anyway a dictionary can have duplicate keys and hold the same or different values. Here is an example of what i'm trying to do:
dict = {
'Key1' : 'Helo', 'World'
'Key1' : 'Helo'
'Key1' : 'Helo', 'World'
}
I tried doing this but when I associate any value to key1, it gets added to the same key1.
Is this possible with a dictionary? If not what other data structure I can use to implement this process?
Use dictionaries of lists to hold multiple values.
One way to have multiple values to a key is to use a dictionary of lists.
x = { 'Key1' : ['Hello', 'World'],
'Key2' : ['Howdy', 'Neighbor'],
'Key3' : ['Hey', 'Dude']
}
To get the list you want (or make a new one), I recommend using setdefault.
my_list = x.setdefault(key, [])
Example:
>>> x = {}
>>> x['abc'] = [1,2,3,4]
>>> x
{'abc': [1, 2, 3, 4]}
>>> x.setdefault('xyz', [])
[]
>>> x.setdefault('abc', [])
[1, 2, 3, 4]
>>> x
{'xyz': [], 'abc': [1, 2, 3, 4]}
Using defaultdict for the same functionality
To make this even easier, the collections module has a defaultdict object that simplifies this. Just pass it a constructor/factory.
from collections import defaultdict
x = defaultdict(list)
x['key1'].append(12)
x['key1'].append(13)
You can also use dictionaries of dictionaries or even dictionaries of sets.
>>> from collections import defaultdict
>>> dd = defaultdict(dict)
>>> dd
defaultdict(<type 'dict'>, {})
>>> dd['x']['a'] = 23
>>> dd
defaultdict(<type 'dict'>, {'x': {'a': 23}})
>>> dd['x']['b'] = 46
>>> dd['y']['a'] = 12
>>> dd
defaultdict(<type 'dict'>, {'y': {'a': 12}, 'x': {'a': 23, 'b': 46}})
I think you want collections.defaultdict:
from collections import defaultdict
d = defaultdict(list)
list_of_values = [['Hello', 'World'], 'Hello', ['Hello', 'World']]
for v in list_of_values:
d['Key1'].append(v)
print d
This will deal with duplicate keys, and instead of overwriting the key, it will append something to that list of values.
Keys are unique to the data. Consider using some other value for a key or consider using a different data structure to hold this data.
for example:
don't use a persons address as a unique key because several people might live there.
a person's social security number or a drivers license is a much better unique id of a person.
you can create your own id to force it to be unique.

how join list tuple and dict into a dict?

how join list tuple and dict into a dict?
['f','b','c','d'] (1,2,3) and {'a':'10'}
d excluded for list be compatible with the tuple
output {'f':'1','b':'2','c':'3','a':'10'}
You can make a dict from keys and values like so:
keys = ['a','b','c','d']
values = (1,2,3)
result = dict(zip(keys, values)) # {'a': 1, 'c': 3, 'b': 2}
Then you can update it with another dict
result.update({ 'f' : 5 })
print result # {'a': 1, 'c': 3, 'b': 2, 'f': 5}
dict(zip(a_list, a_tuple)).update(a_dictionary)
when a_list is your list, a_tuple is your tuple and a_dictionary is your dictionary.
EDIT:
If you really wanted to turn the numbers in you tuple into strings than first do:
new_tuple = tuple((str(i) for i in a_tuple))
and pass new_tuple to the zip function.
This will accomplish the first part of your question:
dict(zip(['a','b','c','d'], (1,2,3)))
However, the second part of your question would require a second definition of 'a', which the dictionary type does not allow. However, you can always set additional keys manually:
>>> d = {}
>>> d['e'] = 10
>>> d
{'e':10}
The keys in a dictionary must be unique, so this part: {'a':'1','a':'10'} is impossible.
Here is code for the rest:
l = ['a','b','c','d']
t = (1,2,3)
d = {}
for key, value in zip(l, t):
d[key] = value
Something like this?
>>> dict({'a':'10'}.items() + (zip(['f','b','c','d'],('1','2','3'))))
{'a': '10', 'c': '3', 'b': '2', 'f': '1'}
Since noone has given an answer that converts the tuple items to str yet
>>> L=['f','b','c','d']
>>> T=(1,2,3)
>>> D={'a':'10'}
>>> dict(zip(L,map(str,T)),**D)
{'a': '10', 'c': '3', 'b': '2', 'f': '1'}
>>> l = ['a','b','c','d']
>>> t = (1,2,3)
>>> d = {'a':'10'}
>>> t = map(str, t) # the OP has requested str values, let's do this first
If you are OK with mutating the original dict, then you can just do this:
>>> d.update(zip(l, t))
or in Python 3.9+ (PEP 584):
>>> d |= zip(l, t)
But if you need to keep the original d intact:
>>> new_d = dict(zip(l, t))
>>> new_d |= d

Categories

Resources