I have a dictionary in the following form
dict = {
"a" : {"a1" : 1},
"b" : {"a2" : 1, "a3" : 2},
"c" : {"a2" : 3, "a4" : 3}
}
and I need the reverse index dictionary, in this form:
inverseDict = {
"a1" : {"a" : 1},
"a2" : {"b" : 1, "c" : 3},
"a3" : {"b" : 2},
"a4" : {"c" : 3}
}
Basically
inverseDict = {dict.value.key : { dict.key : dict.value.value}}
So essentially, I need the keys of the values as keys, and the keys as keys of values, while at the same time joining results for duplicate new keys etc.
I've tried to do
ks = dict.keys()
vals = dict.values()
ks2 = vals.keys()
vals2 = vals.values()
if this makes any sense
But I'm getting an error
'dict_values' object has no attribute 'keys'
Which from what I understand is because dict.values() .keys() .items() return "views" instead of the actual element itself, but I don't know hot to go about fixing this problem.
Also is there a more efficient solution I should consider, because my actual dict is pretty large (~10k keys), and the resulting inverse dict will also be large ( >3k keys)
using collections.defaultdict(dict)
and a double loop it's rather easy:
d = {
"a" : {"a1" : 1},
"b" : {"a2" : 1, "a3" : 2},
"c" : {"a2" : 3, "a4" : 3},
}
import collections
inverted = collections.defaultdict(dict)
for key,subd in d.items():
for k,v in subd.items(): # no inspiration for key/value names...
inverted[k][key] = v
inverted
is
{'a1': {'a': 1},
'a2': {'b': 1, 'c': 3},
'a3': {'b': 2},
'a4': {'c': 3}}
using defaultdict
avoids to test if the entry already exists & creates a dictionary-value if it doesn't. So, just add the key/values brainlessly in the expected order.
Note that those problems where you need to deal items into several objects are hardly solved using comprehensions.
You could use setdefault:
d = {
'a': {'a1': 1},
'b': {'a2': 1, 'a3': 2},
'c': {'a2': 3, 'a4': 3}
}
result = {}
for ok, vs in d.items():
for ik, v in vs.items():
result.setdefault(ik, {})[ok] = v
print(result)
Output
{'a4': {'c': 3}, 'a1': {'a': 1}, 'a2': {'c': 3, 'b': 1}, 'a3': {'b': 2}}
The function setdefault
has a similar effect to the usage of defaultdict
.
Another solution sans standard lib... But Jean-Francois Fabre's answer is more concise and probably easier to modularize. When in doubt, use the standard lib.
OriginalDict = ... (Original dict items)
InvertedDict = {}
for k, v in OriginalDict.items():
for k_, v_ in v.items():
if InvertedDict.get(k_) is None:
InvertedDict[k_] = {}
InvertedDict[k_][k] = v_
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With