Reverse / invert a dictionary mapping

PythonDictionaryMappingReverse

Python Problem Overview


Given a dictionary like so:

my_map = {'a': 1, 'b': 2}

How can one invert this map to get:

inv_map = {1: 'a', 2: 'b'}

Python Solutions


Solution 1 - Python

Python 3+:

inv_map = {v: k for k, v in my_map.items()}

Python 2:

inv_map = {v: k for k, v in my_map.iteritems()}

Solution 2 - Python

Assuming that the values in the dict are unique:

Python 3:

dict((v, k) for k, v in my_map.items())

Python 2:

dict((v, k) for k, v in my_map.iteritems())

Solution 3 - Python

If the values in my_map aren't unique:

Python 3:

inv_map = {}
for k, v in my_map.items():
    inv_map[v] = inv_map.get(v, []) + [k]

Python 2:

inv_map = {}
for k, v in my_map.iteritems():
    inv_map[v] = inv_map.get(v, []) + [k]

Solution 4 - Python

To do this while preserving the type of your mapping (assuming that it is a dict or a dict subclass):

def inverse_mapping(f):
    return f.__class__(map(reversed, f.items()))

Solution 5 - Python

Try this:

inv_map = dict(zip(my_map.values(), my_map.keys()))

(Note that the Python docs on dictionary views explicitly guarantee that .keys() and .values() have their elements in the same order, which allows the approach above to work.)

Alternatively:

inv_map = dict((my_map[k], k) for k in my_map)

or using python 3.0's dict comprehensions

inv_map = {my_map[k] : k for k in my_map}

Solution 6 - Python

Another, more functional, way:

my_map = { 'a': 1, 'b':2 }
dict(map(reversed, my_map.items()))

Solution 7 - Python

We can also reverse a dictionary with duplicate keys using defaultdict:

from collections import Counter, defaultdict

def invert_dict(d):
    d_inv = defaultdict(list)
    for k, v in d.items():
        d_inv[v].append(k)
    return d_inv

text = 'aaa bbb ccc ddd aaa bbb ccc aaa' 
c = Counter(text.split()) # Counter({'aaa': 3, 'bbb': 2, 'ccc': 2, 'ddd': 1})
dict(invert_dict(c)) # {1: ['ddd'], 2: ['bbb', 'ccc'], 3: ['aaa']}  

See here: > This technique is simpler and faster than an equivalent technique using dict.setdefault().

Solution 8 - Python

This expands upon the answer by Robert, applying to when the values in the dict aren't unique.

class ReversibleDict(dict):
    
    def reversed(self):
        """
        Return a reversed dict, with common values in the original dict
        grouped into a list in the returned dict.
        
        Example:
        >>> d = ReversibleDict({'a': 3, 'c': 2, 'b': 2, 'e': 3, 'd': 1, 'f': 2})
        >>> d.reversed()
        {1: ['d'], 2: ['c', 'b', 'f'], 3: ['a', 'e']}
        """
        
        revdict = {}
        for k, v in self.iteritems():
            revdict.setdefault(v, []).append(k)
        return revdict

The implementation is limited in that you cannot use reversed twice and get the original back. It is not symmetric as such. It is tested with Python 2.6. Here is a use case of how I am using to print the resultant dict.

If you'd rather use a set than a list, and there could exist unordered applications for which this makes sense, instead of setdefault(v, []).append(k), use setdefault(v, set()).add(k).

Solution 9 - Python

Combination of list and dictionary comprehension. Can handle duplicate keys

{v:[i for i in d.keys() if d[i] == v ] for k,v in d.items()}

Solution 10 - Python

A case where the dictionary values is a set. Like:

some_dict = {"1":{"a","b","c"},
        "2":{"d","e","f"},
        "3":{"g","h","i"}}

The inverse would like:

some_dict = {vi: k  for k, v in some_dict.items() for vi in v}

The output is like this:

{'c': '1',
 'b': '1',
 'a': '1',
 'f': '2',
 'd': '2',
 'e': '2',
 'g': '3',
 'h': '3',
 'i': '3'}

Solution 11 - Python

For instance, you have the following dictionary:

dict = {'a': 'fire', 'b': 'ice', 'c': 'fire', 'd': 'water'}

And you wanna get it in such an inverted form:

inverted_dict = {'fire': ['a', 'c'], 'ice': ['b'], 'water': ['d']}

First Solution. For inverting key-value pairs in your dictionary use a for-loop approach:

# Use this code to invert dictionaries that have non-unique values

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

Second Solution. Use a dictionary comprehension approach for inversion:

# Use this code to invert dictionaries that have unique values

inverted_dict = {value: key for key, value in dict.items()}

Third Solution. Use reverting the inversion approach (relies on second solution):

# Use this code to invert dictionaries that have lists of values

dict = {value: key for key in inverted_dict for value in my_map[key]}

Solution 12 - Python

Lot of answers but didn't find anything clean in case we are talking about a dictionary with non-unique values.

A solution would be:

from collections import defaultdict

inv_map = defaultdict(list) 
for k, v in my_map.items(): 
    inv_map[v].append(k)
Example:

If initial dict my_map = {'c': 1, 'd': 5, 'a': 5, 'b': 10}

then, running the code above will give:

{5: ['a', 'd'], 1: ['c'], 10: ['b']}

Solution 13 - Python

If the values aren't unique, and you're a little hardcore:

inv_map = dict(
    (v, [k for (k, xx) in filter(lambda (key, value): value == v, my_map.items())]) 
    for v in set(my_map.values())
)

Especially for a large dict, note that this solution is far less efficient than the answer https://stackoverflow.com/questions/483666/python-reverse-inverse-a-mapping/485368#485368 because it loops over items() multiple times.

Solution 14 - Python

In addition to the other functions suggested above, if you like lambdas:

invert = lambda mydict: {v:k for k, v in mydict.items()}

Or, you could do it this way too:

invert = lambda mydict: dict( zip(mydict.values(), mydict.keys()) )

Solution 15 - Python

I think the best way to do this is to define a class. Here is an implementation of a "symmetric dictionary":

class SymDict:
    def __init__(self):
		self.aToB = {}
		self.bToA = {}
	
	def assocAB(self, a, b):
		# Stores and returns a tuple (a,b) of overwritten bindings
		currB = None
		if a in self.aToB: currB = self.bToA[a]
		currA = None
		if b in self.bToA: currA = self.aToB[b]
		
		self.aToB[a] = b
		self.bToA[b] = a
		return (currA, currB)
	
	def lookupA(self, a):
		if a in self.aToB:
			return self.aToB[a]
		return None
	
	def lookupB(self, b):
		if b in self.bToA:
			return self.bToA[b]
		return None

Deletion and iteration methods are easy enough to implement if they're needed.

This implementation is way more efficient than inverting an entire dictionary (which seems to be the most popular solution on this page). Not to mention, you can add or remove values from your SymDict as much as you want, and your inverse-dictionary will always stay valid -- this isn't true if you simply reverse the entire dictionary once.

Solution 16 - Python

This handles non-unique values and retains much of the look of the unique case.

inv_map = {v:[k for k in my_map if my_map[k] == v] for v in my_map.itervalues()}

For Python 3.x, replace itervalues with values.

Solution 17 - Python

I found that this version is more than 10% faster than the accepted version of a dictionary with 10000 keys.

d = {i: str(i) for i in range(10000)}

new_d = dict(zip(d.values(), d.keys()))

Solution 18 - Python

I am aware that this question already has many good answers, but I wanted to share this very neat solution that also takes care of duplicate values:

def dict_reverser(d):
    seen = set()
    return {v: k for k, v in d.items() if v not in seen or seen.add(v)}

This relies on the fact that set.add always returns None in Python.

Solution 19 - Python

Here is another way to do it.

my_map = {'a': 1, 'b': 2}

inv_map= {}
for key in my_map.keys() :
    val = my_map[key]
    inv_map[val] = key

Solution 20 - Python

dict([(value, key) for key, value in d.items()])

Solution 21 - Python

Fast functional solution for non-bijective maps (values not unique):

from itertools import imap, groupby

def fst(s):
    return s[0]

def snd(s):
    return s[1]

def inverseDict(d):
    """
    input d: a -> b
    output : b -> set(a)
    """
    return {
        v : set(imap(fst, kv_iter))
        for (v, kv_iter) in groupby(
            sorted(d.iteritems(),
                   key=snd),
            key=snd
        )
    }

In theory this should be faster than adding to the set (or appending to the list) one by one like in the imperative solution.

Unfortunately the values have to be sortable, the sorting is required by groupby.

Solution 22 - Python

Try this for python 2.7/3.x

inv_map={};
for i in my_map:
    inv_map[my_map[i]]=i	
print inv_map

Solution 23 - Python

Function is symmetric for values of type list; Tuples are coverted to lists when performing reverse_dict(reverse_dict(dictionary))

def reverse_dict(dictionary):
    reverse_dict = {}
    for key, value in dictionary.iteritems():
        if not isinstance(value, (list, tuple)):
            value = [value]
        for val in value:
            reverse_dict[val] = reverse_dict.get(val, [])
            reverse_dict[val].append(key)
    for key, value in reverse_dict.iteritems():
        if len(value) == 1:
            reverse_dict[key] = value[0]
    return reverse_dict

Solution 24 - Python

Since dictionaries require one unique key within the dictionary unlike values, we have to append the reversed values into a list of sort to be included within the new specific keys.

def r_maping(dictionary):
    List_z=[]
    Map= {}
    for z, x in dictionary.iteritems(): #iterate through the keys and values
        Map.setdefault(x,List_z).append(z) #Setdefault is the same as dict[key]=default."The method returns the key value available in the dictionary and if given key is not available then it will return provided default value. Afterward, we will append into the default list our new values for the specific key.
    return Map

Solution 25 - Python

A lambda solution for current python 3.x versions:

d1 = dict(alice='apples', bob='bananas')
d2 = dict(map(lambda key: (d1[key], key), d1.keys()))
print(d2)

Result:

{'apples': 'alice', 'bananas': 'bob'}

This solution does not check for duplicates.

Some remarks:

  • The lambda construct can access d1 from the outer scope, so we only pass in the current key. It returns a tuple.
  • The dict() constructor accepts a list of tuples. It also accepts the result of a map, so we can skip the conversion to a list.
  • This solution has no explicit for loop. It also avoids using a list comprehension for those who are bad at math ;-)

Solution 26 - Python

Taking up the highly voted answer starting If the values in my_map aren't unique:, I had a problem where not only the values were not unique, but in addition, they were a list, with each item in the list consisting again of a list of three elements: a string value, a number, and another number.

Example:

mymap['key1'] gives you:

[('xyz', 1, 2), ('abc', 5, 4)]

I wanted to switch only the string value with the key, keeping the two number elements at the same place. You simply need another nested for loop then:

inv_map = {}
for k, v in my_map.items():
    for x in v:
        # with x[1:3] same as x[1], x[2]:
        inv_map[x[0]] = inv_map.get(x[0], []) + [k, x[1:3]]

Example:

inv_map['abc'] now gives you:

[('key1', 1, 2), ('key1', 5, 4)]

Solution 27 - Python

I would do it that way in python 2.

inv_map = {my_map[x] : x for x in my_map}

Solution 28 - Python

def invertDictionary(d):
    myDict = {}
  for i in d:
     value = d.get(i)
     myDict.setdefault(value,[]).append(i)   
 return myDict
 print invertDictionary({'a':1, 'b':2, 'c':3 , 'd' : 1})

This will provide output as : {1: ['a', 'd'], 2: ['b'], 3: ['c']}

Solution 29 - Python

  def reverse_dictionary(input_dict):
      out = {}
      for v in input_dict.values():  
          for value in v:
              if value not in out:
                  out[value.lower()] = []
  
      for i in input_dict:
          for j in out:
              if j in map (lambda x : x.lower(),input_dict[i]):
                  out[j].append(i.lower())
                  out[j].sort()
      return out

this code do like this:

r = reverse_dictionary({'Accurate': ['exact', 'precise'], 'exact': ['precise'], 'astute': ['Smart', 'clever'], 'smart': ['clever', 'bright', 'talented']})

print(r)

{'precise': ['accurate', 'exact'], 'clever': ['astute', 'smart'], 'talented': ['smart'], 'bright': ['smart'], 'exact': ['accurate'], 'smart': ['astute']}

Solution 30 - Python

Not something completely different, just a bit rewritten recipe from Cookbook. It's futhermore optimized by retaining setdefault method, instead of each time getting it through the instance:

def inverse(mapping):
    '''
    A function to inverse mapping, collecting keys with simillar values
    in list. Careful to retain original type and to be fast.
    >> d = dict(a=1, b=2, c=1, d=3, e=2, f=1, g=5, h=2)
    >> inverse(d)
    {1: ['f', 'c', 'a'], 2: ['h', 'b', 'e'], 3: ['d'], 5: ['g']}
    '''
    res = {}
    setdef = res.setdefault
    for key, value in mapping.items():
        setdef(value, []).append(key)
    return res if mapping.__class__==dict else mapping.__class__(res)

Designed to be run under CPython 3.x, for 2.x replace mapping.items() with mapping.iteritems()

On my machine runs a bit faster, than other examples here

Solution 31 - Python

I wrote this with the help of cycle 'for' and method '.get()' and I changed the name 'map' of the dictionary to 'map1' because 'map' is a function.

def dict_invert(map1):
    inv_map = {} # new dictionary
    for key in map1.keys():
        inv_map[map1.get(key)] = key
    return inv_map

Solution 32 - Python

If values aren't unique AND may be a hash (one dimension):

for k, v in myDict.items():
    if len(v) > 1:
        for item in v:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

And with a recursion if you need to dig deeper then just one dimension:

def digList(lst):
    temp = []
    for item in lst:
        if type(item) is list:
            temp.append(digList(item))
        else:
            temp.append(item)
    return set(temp)

for k, v in myDict.items():
    if type(v) is list:
        items = digList(v)
        for item in items:
            invDict[item] = invDict.get(item, [])
            invDict[item].append(k)
    else:
        invDict[v] = invDict.get(v, [])
        invDict[v].append(k)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBrian M. HuntView Question on Stackoverflow
Solution 1 - PythonSilentGhostView Answer on Stackoverflow
Solution 2 - PythonunbeknownView Answer on Stackoverflow
Solution 3 - PythonRobert RossneyView Answer on Stackoverflow
Solution 4 - Pythonfs.View Answer on Stackoverflow
Solution 5 - PythonsykoraView Answer on Stackoverflow
Solution 6 - PythonBrendan MaguireView Answer on Stackoverflow
Solution 7 - PythonirudyakView Answer on Stackoverflow
Solution 8 - PythonAsclepiusView Answer on Stackoverflow
Solution 9 - PythonSVJView Answer on Stackoverflow
Solution 10 - PythonMondaaView Answer on Stackoverflow
Solution 11 - PythonAndy JazzView Answer on Stackoverflow
Solution 12 - PythonAni MenonView Answer on Stackoverflow
Solution 13 - PythonpcvView Answer on Stackoverflow
Solution 14 - PythonRussellStewartView Answer on Stackoverflow
Solution 15 - PythonNcAdamsView Answer on Stackoverflow
Solution 16 - PythonErsatz KwisatzView Answer on Stackoverflow
Solution 17 - PythonnauerView Answer on Stackoverflow
Solution 18 - PythonmssView Answer on Stackoverflow
Solution 19 - PythonConstantin ShimonenkoView Answer on Stackoverflow
Solution 20 - PythonalgorytmusView Answer on Stackoverflow
Solution 21 - PythoncjayView Answer on Stackoverflow
Solution 22 - PythondhvlnykView Answer on Stackoverflow
Solution 23 - PythonAlfView Answer on Stackoverflow
Solution 24 - PythoneyoeldefareView Answer on Stackoverflow
Solution 25 - PythonmitView Answer on Stackoverflow
Solution 26 - Pythonquestionto42standswithUkraineView Answer on Stackoverflow
Solution 27 - PythongenghiscradeView Answer on Stackoverflow
Solution 28 - PythonRVRView Answer on Stackoverflow
Solution 29 - PythonShb8086View Answer on Stackoverflow
Solution 30 - PythonthodnevView Answer on Stackoverflow
Solution 31 - PythonTaras VoitovychView Answer on Stackoverflow
Solution 32 - PythonmveithView Answer on Stackoverflow