5

I want to change keys to values in a python dictionary, but the values in the original dictionary are not unique.

Here is what I have:

year_person = {2000: ‘Linda’, 2001: ‘Ron’, 2002: ‘Bruce’, 2003: ‘Linda’, 2004: ‘Bruce’, 2005 ‘Gary’, 2006: ‘Linda’}

This is what I want to change it to:

person_year = {‘Linda’: 2000, ‘Ron’: 2001, ‘Bruce’: 2002, ‘Linda’, 2003: ‘Bruce’, 2004 ‘Gary’, 2005: ‘Linda’: 2006}

When I tried to convert it using a for loop, I only got one matching pair for each person.

martineau
  • 119,623
  • 25
  • 170
  • 301
user1311698
  • 101
  • 1
  • 2
  • 3

4 Answers4

11

You can also do it with a defaultdict:

year_person = {2000: 'Linda', 2001: 'Ron', 2002: 'Bruce', 2003: 'Linda', 2004: 'Bruce', 2005: 'Gary', 2006: 'Linda'}

from collections import defaultdict
d = defaultdict(list)
for k, v in year_person.items():
    d[v].append(k)

print dict(d)
>>> {'Bruce': [2002, 2004], 'Linda': [2000, 2003, 2006], 'Ron': [2001], 'Gary': [2005]}
alan
  • 4,752
  • 21
  • 30
  • 5
    If one is going to use a `defaultdict`, it should be converted to a regular `dict` when the function that implements this returns. `defaultdict` has the annoying property of implicitly turning missed attribute hits into insertions. I.e. it creates bugs when that's not what you want. – aaronasterling Apr 06 '12 at 16:37
  • Yes, that's why I converted it in the `print` statement. – alan Apr 06 '12 at 16:44
  • 5
    that's a not exactly transparent way of illustrating the potential problem. – aaronasterling Apr 06 '12 at 16:48
  • You can use dictionary comprehension for this. It might help you dict_two = {value:key for key,value in dict_one.iteritems()} – Syed Shahrukh Ali Gellani Oct 20 '20 at 07:42
6

Just to present some other options and information that may be missing from the current answers:

If you are sure your values are unique, and therefore can become keys, the simplest method is a dict comprehension:

year_person = {2000: 'Linda', 2001: 'Ron', 2002: 'Bruce', 2003: 'Linda', 2004: 'Bruce', 2005: 'Gary', 2006: 'Linda'}
person_year = {key: value for (value, key) in year_person.items()}

Of course, in your case, they are not, so this doesn't work (as it only gives the last value found):

person_year = {'Bruce': 2004, 'Linda': 2006, 'Ron': 2001, 'Gary': 2005}

Instead, we can use a nested list comp inside a dict comp:

{key: [value for value, check_key in year_person.items() if check_key==key] for key in year_person.values()}

Giving us:

{'Bruce': [2002, 2004], 'Linda': [2000, 2003, 2006], 'Ron': [2001], 'Gary': [2005]}

This works, but isn't efficient due to having to loop over the entire dictionary for every entry. A much better solution is the defaultdict solution given by alan, which requires only a single loop.

Community
  • 1
  • 1
Gareth Latty
  • 86,389
  • 17
  • 178
  • 183
3

What you wan't to achieve is technically not feasible. Key to a dictionary cannot be duplicate because if so you cannot uniquely index a dictionary with a key.

What you can do, is to create a dictionary of (key,value) pair where value is the list of all items which has the same key. To achieve it you can do something as follows

>>> person_year={}
>>> [person_year.setdefault(v,[]).append(k) for (k,v) in year_person.iteritems()]
[None, None, None, None, None, None, None]
>>> person_year
{'Bruce': [2002, 2004], 'Linda': [2000, 2003, 2006], 'Ron': [2001], 'Gary': [2005]}
>>> 

Note, if you are only interested in the key value pair and not a dictionary per se' you can just store as a list of tuples as follows

>>> [(v,k) for k,v in year_person.iteritems()]
[('Linda', 2000), ('Ron', 2001), ('Bruce', 2002), ('Linda', 2003), ('Bruce', 2004), ('Gary', 2005), ('Linda', 2006)]
>>>
Abhijit
  • 62,056
  • 18
  • 131
  • 204
  • 1
    I would describe this as unpythonic - a list comprehension is designed to be used to create a list, not to process data. In this case, a much cleaner, clearer solution is the one alan gave with a defaultdict. Also it's worth noting ``iteritems()`` only exists pre-Python 3.x, after that, just use ``items()``. – Gareth Latty Apr 05 '12 at 22:49
2

IMO, defaultdict is unnecessary here and doing it as a list comprehension sacrifices readability (though that's not generally the case). Unless profiling indicates that this is really a bottleneck, I would do it as follows:

def invert_to_lists(dct):
    inverted_dict = {}
    for key in dct:
        inverted_dict.setdefault(dct[key], []).append(key)
    return inverted_dict

defaultdict is one more complication. Using setdefault is fine in this case because it only needs to be typed out once. After going through the rigmarole of importing and instantiating a defaultdict, you will have typed more than making the one call to setdefault.

aaronasterling
  • 68,820
  • 20
  • 127
  • 125