return key by value in dictionary

27,777

Solution 1

Return first matching key:

def find_key(input_dict, value):
    return next((k for k, v in input_dict.items() if v == value), None)

Return all matching keys as a set:

def find_key(input_dict, value):
    return {k for k, v in input_dict.items() if v == value}

Values in a dictionary are not necessarily unique. The first option returns None if there is no match, the second returns an empty set for that case.

Since the order of dictionaries is arbitrary (dependent on what keys were used and the insertion and deletion history), what is considered the 'first' key is arbitrary too.

Demo:

>>> def find_key(input_dict, value):
...     return next((k for k, v in input_dict.items() if v == value), None)
... 
>>> find_key({1:'a', 2:'b', 3:'c', 4:'d'}, 'b')
2
>>> find_key({1:'a', 2:'b', 3:'c', 4:'d'}, 'z') is None
True
>>> def find_key(input_dict, value):
...     return {k for k, v in input_dict.items() if v == value}
... 
>>> find_key({1:'a', 2:'b', 3:'c', 4:'d'}, 'b')
set([2])
>>> find_key({1:'a', 2:'b', 3:'c', 4:'d', 5:'b'}, 'b')
set([2, 5])
>>> find_key({1:'a', 2:'b', 3:'c', 4:'d'}, 'z')
set([])

Note that we need to loop over the values each time we need to search for matching keys. This is not the most efficient way to go about this, especially if you need to match values to keys often. In that case, create a reverse index:

from collections import defaultdict

values_to_keys = defaultdict(set)

for key, value in input_dict:
    values_to_keys[value].add(key)

Now you can ask for the set of keys directly in O(1) (constant) time:

keys = values_to_keys.get(value)

This uses sets; the dictionary has no ordering so either, sets make a little more sense here.

Solution 2

Amend your function as such:

def find_key_for(input_dict, value):    
    for k, v in input_dict.items():
        if v == value:
            yield k

Then to get the first key (or None if not present)

print next(find_key_for(your_dict, 'b'), None)

To get all positions:

keys = list(find_key_for(your_dict, 'b'))

Or, to get 'n' many keys:

from itertools import islice
keys = list(islice(find_key_for(your_dict, 'b'), 5))

Note - the keys you get will be 'n' many in the order the dictionary is iterated.

If you're doing this more often than not (and your values are hashable), then you may wish to transpose your dict

from collections import defaultdict

dd = defaultdict(list)
for k, v in d.items():
    dd[v].append(k)

print dd['b']
Share:
27,777
user2101517
Author by

user2101517

Updated on May 17, 2020

Comments

  • user2101517
    user2101517 about 4 years

    I am trying to return the key in a dictionary given a value

    in this case if 'b' is in the dictionary, I want it to return the key at which 'b' is (i.e 2)

    def find_key(input_dict, value):
        if value in input_dict.values():
            return UNKNOWN            #This is a placeholder
        else:
            return "None"
    
    print(find_key({1:'a', 2:'b', 3:'c', 4:'d'}, 'b'))
    

    The answer I want to get is the key 2, but I am unsure what to put in order to get the answer, any help would be much appreciated

  • Jon Clements
    Jon Clements about 11 years
    +1 - good point about using defaultdict(set) ;)
  • Steven Rumbalski
    Steven Rumbalski about 11 years
    Nice. Also, if values are always unique this can be done without a defaultdict. rev_d = {v:k for k,v in d.iteritems()}
  • mgilson
    mgilson about 11 years
    In the part about transposing the dict, you might also want to add "and the values are unique" as well. Otherwise you don't really get a full transpose. But, nice answer. +1