Reverse Word Search in Python

Is there an easy way to find the key by knowing the meaning in the dictionary?

All I can think of is:

key = [key for key, value in dict_obj.items() if value == 'value'][0] 
+70
python dictionary
Apr 02 '10 at 19:18
source share
13 answers

No. Remember that a value can be found on any number of keys, including 0 or more than 1.

+25
Apr 02 2018-10-02T00:
source share

Your understanding of the list goes through all the dict elements that find all matches, and then simply returns the first key. This generator expression will iterate over only as needed to return the first value:

 key = next(key for key, value in dd.items() if value == 'value') 

where dd is a dict. Raises StopIteration if no match is found, so you can catch it and return a more suitable exception, like ValueError or KeyError .

+60
Apr 02 '10 at 20:40
source share

There are times when a dictionary is one: one mapping

For example,

 d = {1: "one", 2: "two" ...} 

Your approach is approved if you do only one search. However, if you need to do more than one search, creating a reverse dictionary will be more efficient.

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

If it is possible to use several keys with the same value, in this case you need to specify the desired behavior.

If your Python is 2.6 or older, you can use

 ivd = dict((v, k) for k, v in d.items()) 
+39
Apr 02 '10 at 20:40
source share

This version is 26% shorter than yours , but works identically, even for redundant / ambiguous values ​​(returns the first match, like yours). However, it is probably twice as slow as yours because it creates a list from a dict twice.

 key = dict_obj.keys()[dict_obj.values().index(value)] 

Or, if you prefer brevity in reading, you can save another character with

 key = list(dict_obj)[dict_obj.values().index(value)] 

And if you prefer efficiency, it's better to use @PaulMcGuire. If there are many keys that have the same value, it is more efficient not to instantiate this list of keys with a list and use a generator instead:

 key = (key for key, value in dict_obj.items() if value == 'value').next() 
+27
Jul 25 2018-12-12T00:
source share

Maybe a dictionary-like class like DoubleDict below, what do you want? You can use any of the provided metaclasses in conjunction with DoubleDict or not use any metaclass at all.

 import functools import threading ################################################################################ class _DDChecker(type): def __new__(cls, name, bases, classdict): for key, value in classdict.items(): if key not in {'__new__', '__slots__', '_DoubleDict__dict_view'}: classdict[key] = cls._wrap(value) return super().__new__(cls, name, bases, classdict) @staticmethod def _wrap(function): @functools.wraps(function) def check(self, *args, **kwargs): value = function(self, *args, **kwargs) if self._DoubleDict__forward != \ dict(map(reversed, self._DoubleDict__reverse.items())): raise RuntimeError('Forward & Reverse are not equivalent!') return value return check ################################################################################ class _DDAtomic(_DDChecker): def __new__(cls, name, bases, classdict): if not bases: classdict['__slots__'] += ('_DDAtomic__mutex',) classdict['__new__'] = cls._atomic_new return super().__new__(cls, name, bases, classdict) @staticmethod def _atomic_new(cls, iterable=(), **pairs): instance = object.__new__(cls, iterable, **pairs) instance.__mutex = threading.RLock() instance.clear() return instance @staticmethod def _wrap(function): @functools.wraps(function) def atomic(self, *args, **kwargs): with self.__mutex: return function(self, *args, **kwargs) return atomic ################################################################################ class _DDAtomicChecker(_DDAtomic): @staticmethod def _wrap(function): return _DDAtomic._wrap(_DDChecker._wrap(function)) ################################################################################ class DoubleDict(metaclass=_DDAtomicChecker): __slots__ = '__forward', '__reverse' def __new__(cls, iterable=(), **pairs): instance = super().__new__(cls, iterable, **pairs) instance.clear() return instance def __init__(self, iterable=(), **pairs): self.update(iterable, **pairs) ######################################################################## def __repr__(self): return repr(self.__forward) def __lt__(self, other): return self.__forward < other def __le__(self, other): return self.__forward <= other def __eq__(self, other): return self.__forward == other def __ne__(self, other): return self.__forward != other def __gt__(self, other): return self.__forward > other def __ge__(self, other): return self.__forward >= other def __len__(self): return len(self.__forward) def __getitem__(self, key): if key in self: return self.__forward[key] return self.__missing_key(key) def __setitem__(self, key, value): if self.in_values(value): del self[self.get_key(value)] self.__set_key_value(key, value) return value def __delitem__(self, key): self.pop(key) def __iter__(self): return iter(self.__forward) def __contains__(self, key): return key in self.__forward ######################################################################## def clear(self): self.__forward = {} self.__reverse = {} def copy(self): return self.__class__(self.items()) def del_value(self, value): self.pop_key(value) def get(self, key, default=None): return self[key] if key in self else default def get_key(self, value): if self.in_values(value): return self.__reverse[value] return self.__missing_value(value) def get_key_default(self, value, default=None): return self.get_key(value) if self.in_values(value) else default def in_values(self, value): return value in self.__reverse def items(self): return self.__dict_view('items', ((key, self[key]) for key in self)) def iter_values(self): return iter(self.__reverse) def keys(self): return self.__dict_view('keys', self.__forward) def pop(self, key, *default): if len(default) > 1: raise TypeError('too many arguments') if key in self: value = self[key] self.__del_key_value(key, value) return value if default: return default[0] raise KeyError(key) def pop_key(self, value, *default): if len(default) > 1: raise TypeError('too many arguments') if self.in_values(value): key = self.get_key(value) self.__del_key_value(key, value) return key if default: return default[0] raise KeyError(value) def popitem(self): try: key = next(iter(self)) except StopIteration: raise KeyError('popitem(): dictionary is empty') return key, self.pop(key) def set_key(self, value, key): if key in self: self.del_value(self[key]) self.__set_key_value(key, value) return key def setdefault(self, key, default=None): if key not in self: self[key] = default return self[key] def setdefault_key(self, value, default=None): if not self.in_values(value): self.set_key(value, default) return self.get_key(value) def update(self, iterable=(), **pairs): for key, value in (((key, iterable[key]) for key in iterable.keys()) if hasattr(iterable, 'keys') else iterable): self[key] = value for key, value in pairs.items(): self[key] = value def values(self): return self.__dict_view('values', self.__reverse) ######################################################################## def __missing_key(self, key): if hasattr(self.__class__, '__missing__'): return self.__missing__(key) if not hasattr(self, 'default_factory') \ or self.default_factory is None: raise KeyError(key) return self.__setitem__(key, self.default_factory()) def __missing_value(self, value): if hasattr(self.__class__, '__missing_value__'): return self.__missing_value__(value) if not hasattr(self, 'default_key_factory') \ or self.default_key_factory is None: raise KeyError(value) return self.set_key(value, self.default_key_factory()) def __set_key_value(self, key, value): self.__forward[key] = value self.__reverse[value] = key def __del_key_value(self, key, value): del self.__forward[key] del self.__reverse[value] ######################################################################## class __dict_view(frozenset): __slots__ = '__name' def __new__(cls, name, iterable=()): instance = super().__new__(cls, iterable) instance.__name = name return instance def __repr__(self): return 'dict_{}({})'.format(self.__name, list(self)) 
+5
Jul 24 2018-12-12T00:
source share

Since this is still very relevant, the first hit by Google, and I just spend some time on it, I will post my (working in Python 3) solution:

 testdict = {'one' : '1', 'two' : '2', 'three' : '3', 'four' : '4' } value = '2' [key for key in testdict.items() if key[1] == value][0][0] Out[1]: 'two' 

It will give you the first value that matches.

+4
Feb 12 '16 at 15:46
source share

There is not one, as far as I know, but one way to do this is to create a dictation for normal search by key and another dict for reverse search by value.

Here is an example of such an implementation here:

http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/

This means that searching for keys for a value can lead to several results that can be returned as a simple list.

+2
Apr 02 '10 at 19:24
source share

No, you cannot do this effectively without looking at all the keys and checking all your values. For this you need O(n) time. If you need to do a lot of such searches, you will need to do this effectively by building a reverse dictionary (can also be done in O(n) ), and then search inside that dictionary with reverse translation (each search will take an average of O(1) )

Here is an example of how to build a dictionary with reverse translation (which can display one or many images) from a regular dictionary:

 for i in h_normal: for j in h_normal[i]: if j not in h_reversed: h_reversed[j] = set([i]) else: h_reversed[j].add(i) 

For example, if your

 h_normal = { 1: set([3]), 2: set([5, 7]), 3: set([]), 4: set([7]), 5: set([1, 4]), 6: set([1, 7]), 7: set([1]), 8: set([2, 5, 6]) } 

your h_reversed will be

 { 1: set([5, 6, 7]), 2: set([8]), 3: set([1]), 4: set([5]), 5: set([8, 2]), 6: set([8]), 7: set([2, 4, 6]) } 
+2
Nov 21 '14 at 1:13
source share

Cross-cutting values ​​in a dictionary can be objects of any type that they cannot hash or index in any other way. Therefore, finding a key by value is unnatural for this type of collection. Any such request can be made only in O (n) time. Therefore, if this is a common task, you should take a look at some key indexing, for example, Jon sujjested, or perhaps even some spatial index (DB or http://pypi.python.org/pypi/Rtree/ ).

0
Apr 02 '10 at 20:26
source share

I know this may be considered "wasteful", but in this case I often store the key as an extra column in the value record:

 d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) } 

this is a compromise and seems wrong, but it is simple and works and, of course, depends on values, not simple values, but tuples.

0
Apr 03 '10 at 1:43
source share

I use dictionaries as a kind of "database", so I need to find a key that I can reuse. For my case, if the key value is None , then I can use and reuse it without the need to "highlight" another identifier. Just thought I'd share it.

 db = {0:[], 1:[], ..., 5:None, 11:None, 19:[], ...} keys_to_reallocate = [None] allocate.extend(i for i in db.iterkeys() if db[i] is None) free_id = keys_to_reallocate[-1] 

I like this because I don't have to try to catch any errors like StopIteration or IndexError . If there is a key, then free_id will contain it. If this does not happen, then it will be just None . Probably not pythonic, but I really didn't want to use try here ...

0
Jul 19 '17 at 0:31
source share

Since the value may not be present in the dict, more pythonic and automatically documented code would be:

 a # Value to search against x = None # Searched key for k, v in d.items(): if v == a: x = k break x # Now contains the key or None if not found. 

Indeed, dicts are not accepted to answer such a problem, if you encounter this problem in a newly developed program, then you should probably consider your design.

0
Oct 12 '17 at 6:57
source share
 key in dict.values() 

It's literally this

-3
Jun 25 '14 at 7:44
source share



All Articles