Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract duplicate values from a dictionary

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.

like image 586
Monkeys R. Human Avatar asked Mar 21 '12 00:03

Monkeys R. Human


People also ask

Can Python dictionary have duplicate values?

Dictionaries in Python First, a given key can appear in a dictionary only once. Duplicate keys are not allowed.

Can dictionary store duplicate values?

The straight answer is NO. You can not have duplicate keys in a dictionary in Python. But we can have a similar effect as keeping duplicate keys in dictionary.


2 Answers

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'}
like image 179
DSM Avatar answered Oct 18 '22 21:10

DSM


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'}
like image 40
larsks Avatar answered Oct 18 '22 23:10

larsks