Reputation: 239
Is there any simple way to swap python dictionary key values where values are list My dictionary is like following
d={1:[1,2,3,4],2:[2,3,4],5:[1,3,6,7]}
I want to generate a dictionary from it like following
a={1:[1,5],2:[1,2],3:[1,2,5],4:[1,2],6:[5],7:[5]}
I tested with reversed
dict(map(reversed, d.items())
It won't iterate and create keys with items in the list returns TypeError: unhashable type: 'list'
I am looking for any inline methods available for achieving this
Upvotes: 3
Views: 765
Reputation: 531325
This isn't really suitable for a one-liner; you are oversimplifying what you need to do by calling it a simple reversal.
A real reversal would simply map values to keys instead of keys to values, which you can do (with the slight but necessary change of lists to tuples):
>>> print dict((tuple(d[k]), k) for k in d)
{(1, 3, 6, 7): 5, (2, 3, 4): 2, (1, 2, 3, 4): 1}
What you want is far more complicated, and is commonly called a transposition of the dict.
from operator import itemgetter as ig
from itertools import groupby
transposed_dict = dict((k, map(ig(1), v))
for k, v in groupby(
sorted((nk, k) for k in d for nk in d[k]),
key=ig(0)))
There's nothing particularly simple about this, although conceptually it isn't too bad:
(nk, k) for k in d for nk in d[k]
creates an expanded association list from the dictionary, with the keys and values inverted.
groupby
collects all the tuples with a common first element
(k, map(ig(1), v))
collects the tuples with a common first element into a single tuple: (1,1), (1,2) => (1, [1,2])
.
The tuples from step 3 are used to build the new dictionary.
You are far better off, however, using a simple 3-line for
loop as shown by Willem Van Onsem; the essence of that loop is in the generator expression used by sorted
; everything else is just dealing with the attempt to avoid mutable variables. Not everything should (or can) be reduced to a simple one-liner.
Upvotes: 0
Reputation: 24052
This will work:
def revdict(d):
r = {}
for k in d:
for v in d[k]:
if v not in r:
r[v] = [k]
else:
r[v].append(k)
return r
Then you can do:
d={1:[1,2,3,4],2:[2,3,4],5:[1,3,6,7]}
a = revdict(d)
print(a)
If you want to avoid having to check for new keys, you can use a defaultdict
, then always append.
Upvotes: 3
Reputation: 476709
Your approach does not work, since here you aim to construct a dictionary:
{[1,2,3,4]: 1, [2,3,4]: 2, [1,3,6,7]: 5}
but since lists are unhashable, these can not be used as keys (and furthermore it is not what you intend to construct anyway).
You probably better use a defaultdict
for this:
from collections import defaultdict
result = defaultdict(list)
for k, vs in d.items():
for v in vs:
result[v].append(k)
after this opertion, result
is a defaultdict
(a subclass of the vanilla dict
) which maps items in the list of values to keys (that contained that value). Like:
>>> result
defaultdict(<class 'list'>, {1: [1, 5], 2: [1, 2], 3: [1, 2, 5], 4: [1, 2], 6: [5], 7: [5]})
You can optionally use:
result = dict(result)
to create a new dictionary with these values (and thus drop the defaultdict
).
Mind that:
d
should be hashable.Upvotes: 2