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

How to Invert a Dictionary in Python Featured Image

Welcome to the start of the How to Python series. In this series, I’m putting together several articles for small Python problems that can be solved in a few lines of code. This series is inspired by the everyday Google searches I make to solve my own problems at work. To get started, I’ve decided to kick off the series by writing an article on how to invert a dictionary.

In short, one of the best ways to invert a dictionary in Python is to use a for loop in conjunction with the `setdefault`python method of dictionaries to store duplicate keys in a list. If that’s not an issue for you, a dictionary comprehension works great: `{v: k for k, v in my_dict.items()}`python. As always, we’ll take a look at other solutions as well.

Table of Contents

Video Summary

In order to supplement my articles, I’ve been slowly but surely launching accompanying videos. As a matter of fact, this is just my second YouTube video, so show it some love!

In it, I share all of the same solutions to the dictionary inversion problem that you’ll find in this article. Of course, the advantage of a video is that you can see it all live with a bit of my own commentary. If you want to jump to a specific part of the video, I have the timestamps setup in the description.

Problem Introduction

Recently, I was working on a Python project where I needed to invert a dictionary. For all you Boku no Hero AcademiaOpens in a new tab. (BNHA) fans out there, I basically wanted to be able to do the following:

my_dict = {
  'Izuku Midoriya': 'One for All', 
  'Katsuki Bakugo': 'Explosion', 
  'All Might': 'One for All', 
  'Ochaco Uraraka': 'Zero Gravity'
}

my_inverted_dict = {
  'One for All': ['Izuku Midoriya', 'All Might'], 
  'Explosion': ['Katsuki Bakugo'], 
  'Ochaco Uraraka': ['Zero Gravity']
}

In this scenario, we have a dictionary of characters from BNHA mapped to their quirks, and we want to convert that to a dictionary of quirks mapped to the characters that have them.

Unfortunately, the original dictionary has non-unique values, so flipping the dictionary would result in a loss of keys. Instead, we want to accumulate a list of keys for each non-unique value while performing the inversion. As it turns out, this is pretty easy to do.

Note: If you’re looking to perform a reverse dictionary lookup (i.e. get a key given a value), I have a whole other article for that. Otherwise, let’s move on!

Solutions

If we want to invert a dictionary, we have several options. From comprehensions to loops, we’ll take a look at all the practical solutions.

Invert a Dictionary with Map and Reversed

Let’s kick off our series of options with one of the more succinct options:

my_inverted_dict = dict(map(reversed, my_dict.items()))

Here, we use the map function which applies the reversed function to all the items in the dictionary. Then, we take the map object and convert it into a dictionary. The result looks something like the following:

my_inverted_dictionary = {
  'One for All': 'All Might', 
  'Explosion': 'Katsuki Bakugo', 
  'Zero Gravity': 'Ochaco Uraraka'
}

Of course, if we want to make sure we don’t lose Midoriya, we shouldn’t use this method. Otherwise, this would be a perfect solution.

Invert a Dictionary with Zip

According to Capi EtherielOpens in a new tab., there’s yet another way to reverse a dictionary with unique values. Specifically, they mentioned zipping the keys and values in their reverse order and converting the output to a dictionary:

dict(zip(my_dict.values(), my_dict.keys()))

When I first saw this solution, I was a bit skeptical that it would actually work. After all, until Python 3.7, dictionaries had no orderingOpens in a new tab.. In other words, I felt like it wasn’t clear that `values()`python and `keys()`python would return lists that actually lined up. That’s why we typically use a method like `items()`python to ensure we get the keys and values together as pairs.

Well, I kept digging. Apparently, `keys()`python and `values()`python always correspond assuming no changes to the dictionary between calls—at least according to a few folks on Stack OverflowOpens in a new tab.. Of course, be aware that we’ll lose Midoriya with this solution as well:

{
  'One for All': 'All Might', 
  'Explosion': 'Katsuki Bakugo', 
  'Zero Gravity': 'Ochaco Uraraka'
}

In other words, if you have unique values, this might be the option for you.

Invert a Dictionary with a Comprehension

In Python 2.7 and above, we can use a dictionary comprehension to invert a dictionary. Unfortunately, it falls prey to the same issue mentioned before, but it gets the job done for unique values:

my_inverted_dict = {value: key for key, value in my_dict.items()}

Once again, here’s the result of inverting our example dictionary:

my_inverted_dictionary = {
  'One for All': 'All Might', 
  'Explosion': 'Katsuki Bakugo', 
  'Zero Gravity': 'Ochaco Uraraka'
}

As we can see, we lose one of our keys. Perhaps there is a better way to invert a dictionary.

Invert a Dictionary with Defaultdict

Luckily, there is another way! Thanks to our friend, Niels van Galen LastOpens in a new tab., we can accomplish exactly what we need in three lines of code:

from collections import defaultdict
my_inverted_dict = defaultdict(list)
{my_inverted_dict[v].append(k) for k, v in my_dict.items()}

Here, we’ve chosen to leverage the `defaultdict`python object from `collections`python. It allows us to set default values for keys. In this case, we’ve chosen a default value of list, so we can append data without the risk of a runtime error. Check out the results:

my_inverted_dict = {
  'One for All': ['Izuku Midoriya', 'All Might'], 
  'Explosion': ['Katsuki Bakugo'], 
  'Zero Gravity': ['Ochaco Uraraka']
}

Since `defaultdict`python works like a regular `dict`python, we can interact with it all the same. This is great solution if you don’t have any strict dict requirements, and your values are not unique.

All of that said, this solution is considered bad practiceOpens in a new tab. (also, I’d love a source better than Stack Overflow). After all, we are not using the dictionary comprehension as intended. It’s meant to generate a dictionary, but we’re using it to modify an external dictionary.

Invert a Dictionary with a For Loop

Another way to invert a dictionary is to use a for loop. This allows us to iterate over the set of mappings and properly build the new mappings by hand. Take a look:

my_inverted_dict = dict()
for key, value in my_dict.items():
    my_inverted_dict.setdefault(value, list()).append(key)

With this method, we can invert a dictionary while preserving all of our original keys. Let’s take a look at what would happen if we ran this code snippet:

my_inverted_dict = {
  'One for All': ['Izuku Midoriya', 'All Might'], 
  'Explosion': ['Katsuki Bakugo'], 
  'Zero Gravity': ['Ochaco Uraraka']
}

Great! We have exactly what we need, but what happens if we want to return this dictionary to its original form?

Revert the Inversion

In the basic case where all keys and values are unique, we can revert a dictionary back to its original mapping using the same dictionary comprehension we’ve already covered:

my_dict = {value: key for key, value in my_inverted_dict.items()}

Unfortunately, this doesn’t work out with a dictionary that maps keys to lists. That’s because lists in Python are unhashable types. In other words, Python doesn’t allow lists to be keys in dictionaries because lists are not immutableOpens in a new tab..

Fortunately, it’s easier to revert our dictionary than it was to invert it in the first place. We can use the following dictionary comprehension:

my_dict = {value: key for key in my_inverted_dict for value in my_inverted_dict[key]}

As we can see, we make a new key-value pair for every single value in each list using this double loop structure.

Performance

Recently, I thought it would be fun to start adding performance information to these articles. In particular, I’m interested in comparing these solutions by their run time. To do that, we’ll use the timeit library. Before we can run our metrics, we’ll need to setup our solutions in strings:

setup = """
from collections import defaultdict
my_dict = {
  'Izuku Midoriya': 'One for All', 
  'Katsuki Bakugo': 'Explosion', 
  'All Might': 'One for All', 
  'Ochaco Uraraka': 'Zero Gravity'
}
"""

map_and_reversed = """
dict(map(reversed, my_dict.items()))
"""

keys_vals = """
dict(zip(my_dict.values(), my_dict.keys()))
"""

dict_comprehension = """
{value: key for key, value in my_dict.items()}
"""

default_dict = """
my_inverted_dict = defaultdict(list)
{my_inverted_dict[v].append(k) for k, v in my_dict.items()}
"""

brute_force = """
my_inverted_dict = dict()
for key, value in my_dict.items():
    my_inverted_dict.setdefault(value, list()).append(key)
"""

With our strings in hand, we can run our test:

>>> import timeit
>>> min(timeit.repeat(stmt=map_and_reversed, setup=setup))
1.523790699999978
>>> min(timeit.repeat(stmt=keys_vals, setup=setup))
0.745502799999997
>>> min(timeit.repeat(stmt=dict_comprehension, setup=setup))
0.5250495999999885
>>> min(timeit.repeat(stmt=default_dict, setup=setup))
1.5607491000000664
>>> min(timeit.repeat(stmt=brute_force, setup=setup))
1.3153148999999758

As it turns out, the dictionary comprehension is very fast. However, be aware that it can’t handle non-unique values. If that’s not a constraint, then go with the fast solution. Otherwise, it doesn’t really matter which method you choose.

Also, for reference, I ran this on my Windows 10 desktop with Python 3.7.3. If you’re interested in learning more about this performance testing process, I have an article for that.

A Little Recap

Using the methods above, we can invert just about any dictionary.

# Use to invert dictionaries that have unique values
my_inverted_dict = dict(map(reversed, my_dict.items()))

# Use to invert dictionaries that have unique values
my_inverted_dict = dict(zip(my_dict.values(), my_dict.keys()))

# Use to invert dictionaries that have unique values
my_inverted_dict = {value: key for key, value in my_dict.items()}

# Use to invert dictionaries that have non-unique values
from collections import defaultdict
my_inverted_dict = defaultdict(list)
{my_inverted_dict[v].append(k) for k, v in my_dict.items()}

# Use to invert dictionaries that have non-unique values
my_inverted_dict = dict()
for key, value in my_dict.items():
    my_inverted_dict.setdefault(value, list()).append(key)

# Use to invert dictionaries that have lists of values
my_dict = {value: key for key in my_inverted_dict for value in my_inverted_dict[key]}

Just about every other type of dictionary transformation is out of the scope of this tutorial. However, if you have any specific questions, feel free to reach out in the comments.

While you’re here, you might be interested in these articles as well:

If you’re not sure where to start, I recommend my list of Python Code Snippets for Everyday Problems. Alternatively, you can get the latest articles sent to your inbox by becoming a memberOpens in a new tab. or subscribing to the newsletter.

While you’re here, check out some of these related BNHA products on Amazon (ad).

Thanks again for sticking around!

How to Python (42 Articles)—Series Navigation

The How to Python tutorial series strays from the usual in-depth coding articles by exploring byte-sized problems in Python. In this series, students will dive into unique topics such as How to Invert a Dictionary, How to Sum Elements of Two Lists, and How to Check if a File Exists.

Each problem is explored from the naive approach to the ideal solution. Occasionally, there’ll be some just-for-fun solutions too. At the end of every article, you’ll find a recap full of code snippets for your own use. Don’t be afraid to take what you need!

If you’re not sure where to start, I recommend checking out our list of Python Code Snippets for Everyday Problems. In addition, you can find some of the snippets in a Jupyter notebook format on GitHubOpens in a new tab.,

If you have a problem of your own, feel free to ask. Someone else probably has the same problem. Enjoy How to Python!

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Then, he earned a master's in Computer Science and Engineering. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Posts