Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python reverse / inverse a mapping (but with multiple values for each key)

This is really a variation on this question, but not a duplicate:

Python reverse / invert a mapping

Given a dictionary like so:

mydict= { 'a': ['b', 'c'], 'd': ['e', 'f'] }

How can one invert this dict to get:

inv_mydict = { 'b':'a', 'c':'a', 'e':'d', 'f':'d' }

Note that values span uniquely under each key.

Note: I previously had syntax map = ... and dict = ... Reminder not to use map and dict as they are built-in functions, see excellent comments and answers below :)

like image 824
Kevin Lee Avatar asked Apr 14 '15 17:04

Kevin Lee


People also ask

How do you reverse the keys and values in a dictionary?

3) Using loop and reversed() method In this method, you initialize a new empty dictionary for storing the reversed items of the original dictionary. Later, using the for loop you traverse through the original list and store every key-value pair in the new empty dictionary after reversing it using reversed() method.

How do you reverse the order of a key in Python?

Method #1 : Using OrderedDict() + reversed() + items() This method is for older versions of Python. Older versions don't keep order in dictionaries, hence have to converted to OrderedDict to execute this task.

Can you reverse keys and values in dictionary python?

You can also use the keys() and values() method with the for loop to reverse a dictionary. In this approach, we will first make a list of keys and a list of values of the input dictionary. After that, we will make each element of the list of values a key in the output dictionary.

How do I reverse a sorted dictionary?

Use dict() to convert the sorted list back to a dictionary. Use the reverse parameter in sorted() to sort the dictionary in reverse order, based on the second argument.


1 Answers

TL;DR

Use dictionary comprehension, like this

>>> my_map = { 'a': ['b', 'c'], 'd': ['e', 'f'] }
>>> {value: key for key in my_map for value in my_map[key]}
{'c': 'a', 'f': 'd', 'b': 'a', 'e': 'd'}

The above seen dictionary comprehension is functionally equivalent to the following looping structure which populates an empty dictionary

>>> inv_map = {}
>>> for key in my_map:
...     for value in my_map[key]:
...         inv_map[value] = key
... 
>>> inv_map
{'c': 'a', 'f': 'd', 'b': 'a', 'e': 'd'}

Note: Using map shadows the built-in map function. So, don't use that as a variable name unless you know what you are doing.


Other similar ways to do the same

Python 3.x

You can use dict.items, like this

>>> {value: key for key, values in my_map.items() for value in values}
{'c': 'a', 'f': 'd', 'b': 'a', 'e': 'd'}

We use items() method here, which would create a view object from the dictionary which would give key value pairs on iteration. So we just iterate over it and construct a new dictionary with the inverse mapping.

Python 2.x

You can use dict.iteritems like this

>>> {value: key for key, values in my_map.iteritems() for value in values}
{'c': 'a', 'b': 'a', 'e': 'd', 'f': 'd'}

We don't prefer items() method in 2.x, because it will return a list of key-value pairs. We don't want to construct a list just to iterate and construct a new dictionary. That is why we prefer iteritems(), which returns an iterator object which gives a key value pair on iteration.

Note: The actual equivalent of Python 3.x's items would be Python 2.x's viewitems method, which returns a view object. Read more about the view object in the official documentation, here.


iter* vs view* methods in Python 2.x

The main difference between iter* functions and view* functions in Python 2.x is that, the view objects reflect the current state of the dictionary. For example,

>>> d = {1: 2}
>>> iter_items = d.iteritems()
>>> view_items = d.viewitems()

now we add a new element to the dictionary

>>> d[2] = 3

If you try to check if (2, 3) (key-value pair) is in the iter_items, it will throw an error

>>> (2, 3) in iter_items
Traceback (most recent call last):
  File "<input>", line 1, in <module>
RuntimeError: dictionary changed size during iteration

but view object will reflect the current state of the dictionary. So, it will work fine

>>> (2, 3) in view_items
True
like image 80
thefourtheye Avatar answered Oct 14 '22 18:10

thefourtheye