GBA13
GBA13

Reputation: 215

Reversing a Dict

I am currently trying to make a function which reverses a dict's keys and values. I was looking online and came across this:

def reverse(d):
    return dict([(v, k) for k, v in d.iteritems()])

My problem is that I'm not sure what this means. I understand the idea of a for loop on the single line but I'm not sure how the (v, k) for k, v leads to the keys and values being reversed. Could someone please offer me a hand. (I did search for this, both online and on Stack Overflow but couldn't find anything.)

Upvotes: 1

Views: 200

Answers (3)

Padraic Cunningham
Padraic Cunningham

Reputation: 180471

for k, v in d.iteritems() is each key k and value v so reversing v and k with (v, k) makes the old value the key and the old key the new value

In [7]: d = {1:10,2:20}

In [8]: d.items()
Out[8]: dict_items([(1, 10), (2, 20)]) # tuples of key and value

 In [1]: d = {1:10,2:20}

In [2]: for k,v in d.iteritems():
           print k,v
   ...:     
1 10 # 1 is the key 10 is the value
2 20
In [3]: new_d = {v:k for k,v in d.iteritems()} # swap key for value and value for key

In [4]: new_d
Out[4]: {10: 1, 20: 2}

Two problems you may encounter are duplicate values or values that are not hashable so they cannot be used as keys like lists, sets etc...

In [5]: d = {1:2,2:2}

In [6]: new_d = {v:k for k,v in d.iteritems()}

In [7]: new_d
Out[7]: {2: 2} # now only one key and value in the dict

In [8]: d = {1:2,2:[2]}

In [9]: new_d = {v:k for k,v in d.iteritems()}
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-9-46a3901ce850> in <module>()
----> 1 new_d = {v:k for k,v in d.iteritems()}

<ipython-input-9-46a3901ce850> in <dictcomp>((k, v))
----> 1 new_d = {v:k for k,v in d.iteritems()}

TypeError: unhashable type: 'list'

dict([(v, k) for k, v in d.iteritems()]) will have the same output as {v:k for k,v in d.iteritems()}, the main difference is the former is also compatible with python < 2.7.

If you were using python < 2.7 there is no need to use a list you can just use a generator expression:

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

Upvotes: 4

Marius
Marius

Reputation: 60150

OK, so when you call iteritems() on a dict, it gives you a (key, value) tuple for each item in your dictionary:

for item in d.iteritems():
    print(item)

Then you can assign each item in the tuple to a separate variable using Python's tuple unpacking syntax:

a, b = (1, 2)
print(a)  # 1
print(b)  # 2

And if you pass a list of tuples to dict(), it treats them as a list of (key, value) items:

eg_dict = dict([(a, 4), (b, 6)])
print(eg_dict)

Finally, the example you posted makes use of Python's list comprehension syntax:

item_list = ['item' + str(n) for n in range(1, 6)]
print(item_list)

To understand the code snippet you've posted, you just need to be familiar with these Python idioms. If you haven't seen any of these techniques before then it's a fairly dense burst of new information to get your head around.

Upvotes: 1

brobas
brobas

Reputation: 606

the dict constructor can receive an iterable of key/value pairs to create a dictionary, so this code is saying "grab the key/value pairs from this dictionary d and create a new dictionary where the values of d are now the keys and the keys of d become the values"

That is why that the (v,k) are reversed, if you did NOT reverse them, like this

 def reverse(d):
    return dict([(k, v) for k, v in d.iteritems()])

you would get an identical dictionary back.

also note that in python 2.7 and later you can actually use the even more compact:

{v:k for k,v in d.items()}

Which reads more intuitively (at least to me) because it looks more like a list comprehension, only it creates a dict.

Upvotes: 1

Related Questions