index python dictionary by value

Go To


Possible Duplicate:
Inverse dictionary lookup - Python

Is there a built in way to index a dictionary by value in Python.

e.g. something like:

dict = {'fruit':'apple','colour':'blue','meat':'beef'}
print key where dict[key] == 'apple'


dict = {'fruit':['apple', 'banana'], 'colour':'blue'}
print key where 'apple' in dict[key]

or do I have to manually loop it?

2012-04-04 16:41
by Ferguzz
Be warned that there is no restriction that the values be unique in the dict, only the keys. What happens when you have two identical keys in your dict - Hooked 2012-04-04 16:44
@Hooked Do you mean "two identical values" - Kris Harper 2012-04-04 16:45
@root45 yes I mean identical "values" not "keys", sorry about that. Something like: A = {1:'foo',2:'foo'}. What is the inverse of foo supposed to return here - Hooked 2012-04-04 16:46
I suppose a list of keys should be returned in that case. good point though - Ferguzz 2012-04-04 16:47
@Ferguzz: Wrote you an answer which works in all cases and returns a list of keys so that duplicates are handled properly - Niklas B. 2012-04-04 17:03
@casperOne: I don't agree. This problem is a more complex than the "duplicate - Niklas B. 2012-04-06 15:12


You could use a list comprehension:

my_dict = {'fruit':'apple','colour':'blue','meat':'beef'}
print [key for key, value in my_dict.items() if value == 'apple']

The code above is doing almost exactly what said you want:

print key where dict[key] == 'apple'

The list comprehension is going through all the key, value pairs given by your dictionary's items method, and making a new list of all the keys where the value is 'apple'.

As Niklas pointed out, this does not work when your values could potentially be lists. You have to be careful about just using in in this case since 'apple' in 'pineapple' == True. So, sticking with a list comprehension approach requires some type checking. So, you could use a helper function like:

def equals_or_in(target, value):
    """Returns True if the target string equals the value string or,
    is in the value (if the value is not a string).
    if isinstance(target, str):
        return target == value
        return target in value

Then, the list comprehension below would work:

my_dict = {'fruit':['apple', 'banana'], 'colour':'blue'}
print [key for key, value in my_dict.items() if equals_or_in('apple', value)]
2012-04-04 16:44
by Wilduck
I believe OP wants to search the values not the keys - Amjith 2012-04-04 16:53
@Amjith Yeah, I had a brain fart. It's been fixed - Wilduck 2012-04-04 16:54
Doesn't work in the more general second exampl - Niklas B. 2012-04-04 16:56
Rather than checking for string type, I'd check for list type, which is less likely to change, otherwise your function will not work for integer values, for example. Also, you should use isinstance instance of comparing type() - Niklas B. 2012-04-04 17:31
The issue with checking for a list type is that you no longer can check for membership in a set. Where the correct answer falls is up to the OP. You're definitely right about isinstance though. I'll update - Wilduck 2012-04-04 18:20


You'll have to manually loop it, but if you'll need the lookup repeatedly this is a handy trick:

d1 = {'fruit':'apple','colour':'blue','meat':'beef'}

d1_rev = dict((v, k) for k, v in d1.items())

You can then use the reverse dictionary like this:

>>> d1_rev['blue']
>>> d1_rev['beef']
2012-04-04 16:43
by g.d.d.c
You should at least mention what the last line does - Niklas B. 2012-04-04 16:44
meh. looks pretty obviou - bernie 2012-04-04 16:45
@bernie: It's not obvious to OP, otherwise he wouldn't ask. A simple "builds the reverse dictionary (value->key)" would be enough, but without anything, this is a bit confusin - Niklas B. 2012-04-04 16:45
that wouldn't work with the second example because lists are mutable, and so therefore can't be used as dictionary keys - Jacob 2012-04-04 16:46
if dict is large, use .iteritems() instea - bernie 2012-04-04 16:47
@bernie: Only in Python 2. As it stands, this works in all versions of Python (even <2.7), which is nic - Niklas B. 2012-04-04 16:47
This won't work if the value is a list. Lists are not hashable - Amjith 2012-04-04 16:52
It would also not work if as expected if lists were hashabl - Niklas B. 2012-04-04 16:56


Your requirements are more complex than you realize:

  • You need to handle both list values and plain values
  • You don't actually need to get back a key, but a list of keys

You could solve this in two steps:

  1. normalize the dict so that every value is a list (every plain value becomes a single-element)
  2. build a reverse dictionary

The following functions will solve this:

from collections import defaultdict

def normalize(d):
    return { k:(v if isinstance(v, list) else [v]) for k,v in d.items() }

def build_reverse_dict(d):
    res = defaultdict(list)
    for k,values in normalize(d).items():
        for x in values:
    return dict(res)

To be used like this:

>>> build_reverse_dict({'fruit':'apple','colour':'blue','meat':'beef'})
{'blue': ['colour'], 'apple': ['fruit'], 'beef': ['meat']}
>>> build_reverse_dict({'fruit':['apple', 'banana'], 'colour':'blue'})
{'blue': ['colour'], 'apple': ['fruit'], 'banana': ['fruit']}
>>> build_reverse_dict({'a':'duplicate', 'b':['duplicate']})
{'duplicate': ['a', 'b']}

So you just build up the reverse dictionary once and then lookup by value and get back a list of keys.

2012-04-04 16:50
by Niklas B.
I think there is a typo at res[x] as you've never defined x - Hooked 2012-04-04 17:11
@Hooked: Thanks, indeed I had edited to make it shorter and broke it by doing so.. - Niklas B. 2012-04-04 17:15