Fairly new to Python, still struggling with so much information.
All the documentation I've seen about dictionaries explain various ways of getting a value via a key - but I'm looking for a pythonic way to do the opposite - get a key via a value.
I know I can loop through the keys and inspect their values until I find the value I'm looking for and then grab the key, but I'm looking for a direct route.
There is no direct route. It's pretty easy with list comprehensions, though;
[k for k, v in d.iteritems() if v == desired_value]
If you need to do this occasionally and don't think it's worth while indexing it the other way as well, you could do something like:
class bidict(dict):
def key_with_value(self, value, default=None):
for k, v in self.iteritems():
if v == value:
return v
return default
def keys_with_value(self, value, default=None):
return [v for k, v in self.iteritems() if v == value]
Then d.key_with_value
would behave rather like d.get
, except the other way round.
You could also make a class which indexed it both ways automatically. Key and value would both need to be hashable, then. Here are three ways it could be implemented:
In two separate dicts, with the exposing some dict-like methods; you could perhaps do foo.by_key[key]
or foo.by_value[value]
. (No code given as it's more complicated and I'm lazy and I think this is suboptimal anyway.)
In a different structure, so that you could do d[key]
and d.inverse[value]
:
class bidict(dict):
def __init__(self, *args, **kwargs):
self.inverse = {}
super(bidict, self).__init__(key, value)
def __setitem__(self, key, value):
super(bidict, self).__setitem__(key, value)
self.inverse[value] = key
def __delitem__(self, key):
del self.inverse[self[key]]
super(bidict, self).__delitem__(key)
In the same structure, so that you could do d[key]
and d[value]
:
class bidict(dict):
def __setitem__(self, key, value):
super(bidict, self).__setitem__(key, value)
super(bidict, self).__setitem__(value, key)
def __delitem__(self, key):
super(bidict, self).__delitem__(self[key])
super(bidict, self).__delitem__(key)
(Notably absent from these implementations of a bidict
is the update
method which will be slightly more complex (but help(dict.update)
will indicate what you'd need to cover). Without update
, bidict({1:2})
wouldn't do what it was intended to, nor would d.update({1:2})
.)
Also consider whether some other data structure would be more appropriate.
Since your dictionary can contain duplicate values (i.e. {'a': 'A', 'b': 'A'}
), the only way to find a key from value is to iterate over the dictionary as you describe.
Or... build the opposite dictionary. you have to recreate it after each modification of the original dictionary.
Or... write a class that maintains both-ways dictionary. You will have to manage situations where duplicate value appears.
the first solution with the list comprehension is good.
but a small fix for python 3.x, instead of .iteritems()
it should be just .items()
:
[k for k, v in d.items() if v == desired_value]
Building a opposite dictionary is not at all good manner as one or more key have same value but if you invert it you need to insert key:[value1,... ] structure which will lead you to another problem.