Есть ли простой способ найти ключ, зная значение в словаре?Поиск в исходном словаре в Python
Все, что я могу думать, это:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
Есть ли простой способ найти ключ, зная значение в словаре?Поиск в исходном словаре в Python
Все, что я могу думать, это:
key = [key for key, value in dict_obj.items() if value == 'value'][0]
Там нет. Не забывайте, что значение может быть найдено на любом количестве ключей, в том числе 0 или больше, чем 1.
python имеет метод .index для списков возвращает первый найденный индекс с указанным значением или исключение, если не найден ... любая причина, почему такая семантика не может быть применена к словарям? –
@BrianJack: словари не упорядочены, как наборы. Посмотрите на collection.OrderedDict для реализации, которая * * упорядочена. –
. Индекс должен только гарантировать, что он возвращает одно значение, и ему не нужно лексически сначала только, чтобы оно было первым совпадением и что его поведение стабильно (несколько вызовов на одном и том же дикторе с течением времени должны давать один и тот же элемент соответствия). Если словари не изменят свои неизмененные хэши с течением времени, так как другие элементы будут добавлены, удалены или изменены, они все равно будут работать соответствующим образом. Наивная реализация: dictObject.items(). Index (key) –
Существует не один, насколько я знаю, в одном случае, однако, это сделать, чтобы создать dict для обычного поиска по ключу и другого dict для обратного поиска по значению.
Там пример такой реализации здесь:
http://code.activestate.com/recipes/415903-two-dict-classes-which-can-lookup-keys-by-value-an/
Это означает, что ищет ключи для значения может привести к нескольким результатам, которые могут быть возвращены в виде простого списка.
Обратите внимание, что существует много, много возможных значений, которые не являются допустимыми. –
Через значения в словаре могут быть объекты любого типа, которые они не могут хэшировать или индексировать другим способом. Поэтому поиск ключа по значению является неестественным для этого типа коллекции. Любой такой запрос может быть выполнен только в O (n) времени. Поэтому, если это частая задача, вы должны взглянуть на некоторую индексацию ключа, например, Jon sujjested или, возможно, даже некоторый пространственный индекс (DB или http://pypi.python.org/pypi/Rtree/).
Есть случаи, когда словарь является один: один отображение
Например,
d = {1: "one", 2: "two" ...}
Ваш подход хорошо, если вы только делаете один поиск. Однако, если вам нужно сделать больше, чем один поиск будет более эффективным для создания инверсной словарю
ivd = {v: k for k, v in d.items()}
Если есть возможность несколько ключей с тем же значением, что вам нужно будет указать желаемое поведение в этом дело.
Если Питон 2.6 или старше, вы можете использовать
ivd = dict((v, k) for k, v in d.items())
Хорошая оптимизация. Но, я думаю, вы хотели превратить свой список 2-х кортежей в словарь с помощью dict(): 'ivd = dict ([(v, k) для (k, v) в d.items()])' – hobs
Это работает, пока все значения хешируются. – askewchan
@hobs просто используют понимание dict, а не понимание списка: 'invd = {v: k для k, v в d.items()}' – askewchan
Ваш список понимание проходит через все пункты в Словаре в находящих все матчи, то просто возвращает первый ключ. Это выражение генератор будет только итерацию по мере необходимости, чтобы вернуть первое значение:
key = next(key for key, value in dd.items() if value == 'value')
где dd
является ДИКТ. Поднимет StopIteration
, если совпадение не найдено, поэтому вы можете поймать это и вернуть более подходящее исключение, например ValueError
или KeyError
.
Да Должно возникнуть такое же исключение, как listObject.index (key), когда ключ отсутствует в списке. –
также 'keys = {ключ для ключа, значение в dd.items(), если value == 'value'}', чтобы получить набор всех ключей, если несколько совпадений. – askewchan
@askewchan - нет реальной необходимости возвращать это как набор, ключи ключей уже должны быть уникальными, просто верните список - или лучше, верните выражение генератора и позвольте вызывающему его помещать в любой контейнер, который они хотят. – PaulMcG
Я знаю, что это может рассматриваться как «расточительными», но в этом случае я часто хранить ключ в качестве дополнительного столбца в записи значения:
d = {'key1' : ('key1', val, val...), 'key2' : ('key2', val, val...) }
это компромисс и чувствует себя неправильно, но это просто и работает и, конечно, зависит от значений, а не простых значений.
Предполагая, что я это сделаю, вы можете объяснить, как это поможет мне сделать обратный поиск? – Mahesh
Возможно, такой словарь-подобный класс, как DoubleDict
ниже, что вы хотите?Вы можете использовать любой из предоставленных метаклассов в сочетании с DoubleDict
или вообще избегать использования какого-либо метакласса.
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))
OVERKILL! (Мне нравится!) – Lepi
Эта версия на 26% короче, чем yours но функций тождественно, даже для избыточных/неоднозначных значений (возвращает первый матч, как ваша делает). Однако он, вероятно, в два раза медленнее вашего, потому что он дважды создает список из dict.
key = dict_obj.keys()[dict_obj.values().index(value)]
Или, если вы предпочитаете краткость над читаемость вы можете сохранить еще один символ с
key = list(dict_obj)[dict_obj.values().index(value)]
А если вы предпочитаете эффективность, @ PaulMcGuire в approach лучше. Если есть много ключей, которые разделяют то же значение, что это более эффективно не экземпляр этого списка ключей со списком понимания и вместо этого использовать использовать генератор:
key = (key for key, value in dict_obj.items() if value == 'value').next()
key in dict.values()
Это буквально это
инструкция непонятный. «Истина» не является ключом в оригинальном словаре. – FlipMcF
Нет, вы не можете сделать это эффективно, не глядя на все ключи и не проверяя все свои ценности. Для этого вам понадобится O(n)
времени. Если вам нужно сделать много таких поисков, вам нужно будет сделать это эффективно, построив перевернутый словарь (можно сделать также в O(n)
), а затем выполнить поиск внутри этого обратного словаря (каждый поиск будет принимать в среднем O(1)
).
Вот пример того, как построить обращенную словарь (который будет в состоянии сделать один ко многим отображения) из нормального словаря:
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)
Например, если ваш
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])
}
ваш h_reversed
будет
{
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])
}
Поскольку это по-прежнему актуально, первый Google ударил, и я просто потратить некоторое время выяснить это, я выложу мой (работает в Python 3) решение:
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'
Это даст вам первое значение, которое соответствует.
Я использую словари как своего рода «базу данных», поэтому мне нужно найти ключ, который я могу повторно использовать. Для моего случая, если значение ключа равно None
, я могу взять его и повторно использовать без необходимости «выделять» другой идентификатор. Просто подумал, что я поделюсь им.
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]
Мне нравится этот, потому что я не имею, чтобы попытаться поймать какие-либо ошибки, такие как StopIteration
или IndexError
. Если есть ключ, то free_id
будет содержать его. Если этого не произойдет, то это будет просто None
. Наверное, не pythonic, но я действительно не хотел использовать try
здесь ...
В стоимости может быть несуществующей в Словаре, более вещий и авто документированного код будет:
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.
Действительно dicts не сделали, чтобы ответить на такую проблематику, если вы столкнулись с этой проблемой на новая разработанная программа, то вам, вероятно, следует рассмотреть свой дизайн.
Возможный дубликат: http://stackoverflow.com/questions/483666/python-reverse-inverse-a-mapping –
Взгляните на мой ответ [как создать обратный словарь] (http://stackoverflow.com)/a/27052594/1090562) –
Google направил меня сюда ... И я должен сказать ... почему никто не использует 'iteritems', так как для меня это делает разницу в 40 раз быстрее ... с использованием метода() .next – Mayhem