DEV Community

Cover image for How to Invert a Dictionary in Python: Comprehensions, Defaultdict, and More

How to Invert a Dictionary in Python: Comprehensions, Defaultdict, and More

Jeremy Grifski on February 21, 2019

Article was originally posted on therenegadecoder.com where it's actively maintained. In this series, I’m putting together several articles for sm...
Collapse
 
mikedh profile image
Michael Dawson-Haggerty

You can also use zip if you don't mind non-unique values being merged:

In [11]: d = {'hi': 'stuff'}

In [12]: dict(zip(d.values(), d.keys()))
Out[12]: {'stuff': 'hi'}
Collapse
 
renegadecoder94 profile image
Jeremy Grifski

Good point! I always hesitate with this solution because it’s not immediately clear that the two collections would maintain their order. I know they do, but it still bothers me.

Collapse
 
mikedh profile image
Michael Dawson-Haggerty

Yeah, the dict comprehension is the clearest way of doing this. zip is mildly faster but I agree calling keys and values separately is slightly offputting:

In [7]: d = {a: b for a,b in (np.random.random((100000, 2)) * 1e6).astype(int)}

In [8]: %timeit {v: k for k, v in d.items()}
100 loops, best of 3: 12.3 ms per loop

In [9]: %timeit dict(zip(d.values(), d.keys()))
100 loops, best of 3: 10.8 ms per loop

Collapse
 
nielsgl profile image
Niels van Galen Last • Edited

Inverting the original dict with non-unique values can be done cleaner (without the explicit two line for loop and IMO easier to read) using a defaultdict:

>>> from collections import defaultdict
>>> # Original dict
>>> my_dict = {
  'Izuku Midoriya': 'One for All',
  'Katsuki Bakugo': 'Explosion',
  'All Might': 'One for All',
  'Ochaco Uraraka': 'Zero Gravity'
}
>>> my_dict_inverted = defaultdict(list)

>>> [my_dict_inverted[v].append(k) for k, v in my_dict.items()]

>>> my_dict_inverted
defaultdict(<class 'list'>, {'One for All': ['Izuku Midoriya', 'All Might'],
   'Explosion': ['Katsuki Bakugo'], 'Zero Gravity': ['Ochaco Uraraka']})

Or to make it more intuitive denote the comprehension using a dict since that is what we are applying the operation to:

>>> my_dict_inverted = defaultdict(list)

>>> {my_dict_inverted[v].append(k) for k, v in my_dict.items()}

>>> my_dict_inverted
defaultdict(<class 'list'>, {'One for All': ['Izuku Midoriya', 'All Might'],
   'Explosion': ['Katsuki Bakugo'], 'Zero Gravity': ['Ochaco Uraraka']})
Collapse
 
renegadecoder94 profile image
Jeremy Grifski • Edited

Great addition! I would prefer to maintain the dict type and leave out any extraneous imports, but this gets the job done and is probably less error prone.

Mind if I add it to the list?

Collapse
 
nielsgl profile image
Niels van Galen Last

Go ahead :)

It's part of the standard library and it's still a dict so you can use any it like a normal dict, e.g. my_dict_inverted['One for All'] as well as all methods from a normal dict like keys(), values() and items().

Thread Thread
 
renegadecoder94 profile image
Jeremy Grifski

Hi again! I'm in the process of updating this article, and I just realized that this answer has a minor problem. We're using a dictionary comprehension in a bad way. We're updating some external dictionary while the comprehension will also generate a dictionary (in this case, {None}). I believe that's considered bad practice, so I've added a note in the original article.