Получить ключ по значению в словаре


Я сделал функцию, которая будет искать возрастов в Dictionary и показать соответствующее имя:

dictionary = {'george' : 16, 'amber' : 19}
search_age = raw_input("Provide age")
for age in dictionary.values():
    if age == search_age:
        name = dictionary[age]
        print name

Я знаю, как сравнить и найти возраст я просто не знаю, как показать имя человека. Кроме того, я получаю KeyError из-за линии 5. Я знаю, что это не правильно, но я не могу понять, как заставить его искать в обратном направлении.

30 399

30 ответов:

нет. dict не предназначен для использования таким образом.

for name, age in dictionary.items():    # for name, age in dictionary.iteritems():  (for Python 2.x)
    if age == search_age:
        print(name)
mydict = {'george':16,'amber':19}
print mydict.keys()[mydict.values().index(16)] # Prints george

или в Python 3.x:

mydict = {'george':16,'amber':19}
print(list(mydict.keys())[list(mydict.values()).index(16)]) # Prints george

в основном, он разделяет значения словаря в списке, находит позицию значения, которое у вас есть, и получает ключ в этой позиции.

подробнее о keys() и .values() в Python 3: Python: самый простой способ получить список значений из dict?

если вы хотите, чтобы имя и возраст, вы должны использовать .items() что дает вам ключ (key, value) кортежи:

for name, age in mydict.items():
    if age == search_age:
        print name

вы можете распаковать Кортеж в две отдельные переменные прямо в for цикл, затем соответствует возрасту.

вы также должны рассмотреть возможность реверсирования словаря, если вы, как правило, будете искать по возрасту, и нет двух человек одного возраста:

{16: 'george', 19: 'amber'}

так что вы можете посмотреть имя для возраста просто делать

mydict[search_age]

я называю это mydict вместо list, потому что list - имя встроенного типа, и вы не должны использовать это имя для чего-либо еще.

вы даже можете получить список всех людей с заданным возрастом в одной строке:

[name for name, age in mydict.items() if age == search_age]

или если есть только один человек, с каждого возраста:

next((name for name, age in mydict.items() if age == search_age), None)

который просто даст вам None если нет никого с таким возрастом.

наконец, если dict is долго и вы находитесь на Python 2, вы должны рассмотреть возможность использования .iteritems() вместо .items() как Cat Plus Plus сделал в своем ответе, так как ему не нужно делать копию списка.

я подумал, что было бы интересно указать, какие методы являются самыми быстрыми, и в каком сценарии:

вот некоторые тесты, которые я провел (на MacBook Pro 2012 года)

>>> def method1(list,search_age):
...     for name,age in list.iteritems():
...             if age == search_age:
...                     return name
... 
>>> def method2(list,search_age):
...     return [name for name,age in list.iteritems() if age == search_age]
... 
>>> def method3(list,search_age):
...     return list.keys()[list.values().index(search_age)]

результаты profile.run() на каждом методе 100000 раз:

Способ 1:

>>> profile.run("for i in range(0,100000): method1(list,16)")
     200004 function calls in 1.173 seconds

Способ 2:

>>> profile.run("for i in range(0,100000): method2(list,16)")
     200004 function calls in 1.222 seconds

Способ 3:

>>> profile.run("for i in range(0,100000): method3(list,16)")
     400004 function calls in 2.125 seconds

таким образом, это показывает, что для небольшого диктатора метод 1 является самым быстрым. Это скорее всего потому, что он возвращает первый совпадение, в отличие от всех совпадений, таких как метод 2 (см. Примечание ниже).


интересно, что выполняя одни и те же тесты на дикт у меня с 2700 записей, я получаю совершенно разные результаты (на этот раз запустить 10000 раз):

Способ 1:

>>> profile.run("for i in range(0,10000): method1(UIC_CRS,'7088380')")
     20004 function calls in 2.928 seconds

Способ 2:

>>> profile.run("for i in range(0,10000): method2(UIC_CRS,'7088380')")
     20004 function calls in 3.872 seconds

Способ 3:

>>> profile.run("for i in range(0,10000): method3(UIC_CRS,'7088380')")
     40004 function calls in 1.176 seconds

так вот, метод 3 много быстрее. Просто идет, чтобы показать размер вашего дикт будет влиять на какой метод вы выбирать.

заметки: Метод 2 возвращает список все имена, тогда как методы 1 и 3 возвращают только первое совпадение. Я не рассматривал использование памяти. Я не уверен, что метод 3 создает 2 дополнительных списка (ключи () и значения ()) и сохраняет их в памяти.

однострочная версия: (i-Старый словарь, p-перевернутый словарь)

пояснение: i. keys() и i.values () возвращает два списка с ключами и значениями словаря соответственно. Функция zip имеет возможность связывать вместе списки для создания словаря.

предупреждение : это будет работать только если значения hashable и уникальным.

p = dict(zip(i.values(),i.keys()))
a = {'a':1,'b':2,'c':3}
{v:k for k, v in a.items()}[1]

или лучше

{k:v for k, v in a.items() if v == 1}
lKey = [key for key, value in lDictionary.iteritems() if value == lValue][0]

вот мой взгляд на эту проблему. :) Я только начал изучать Python, поэтому я называю это:

"понятное для начинающих" решение.

#Code without comments.

list1 = {'george':16,'amber':19, 'Garry':19}
search_age = raw_input("Provide age: ")
print
search_age = int(search_age)

listByAge = {}

for name, age in list1.items():
    if age == search_age:
        age = str(age)
        results = name + " " +age
        print results

        age2 = int(age)
        listByAge[name] = listByAge.get(name,0)+age2

print
print listByAge

.

#Code with comments.
#I've added another name with the same age to the list.
list1 = {'george':16,'amber':19, 'Garry':19}
#Original code.
search_age = raw_input("Provide age: ")
print
#Because raw_input gives a string, we need to convert it to int,
#so we can search the dictionary list with it.
search_age = int(search_age)

#Here we define another empty dictionary, to store the results in a more 
#permanent way.
listByAge = {}

#We use double variable iteration, so we get both the name and age 
#on each run of the loop.
for name, age in list1.items():
    #Here we check if the User Defined age = the age parameter 
    #for this run of the loop.
    if age == search_age:
        #Here we convert Age back to string, because we will concatenate it 
        #with the person's name. 
        age = str(age)
        #Here we concatenate.
        results = name + " " +age
        #If you want just the names and ages displayed you can delete
        #the code after "print results". If you want them stored, don't...
        print results

        #Here we create a second variable that uses the value of
        #the age for the current person in the list.
        #For example if "Anna" is "10", age2 = 10,
        #integer value which we can use in addition.
        age2 = int(age)
        #Here we use the method that checks or creates values in dictionaries.
        #We create a new entry for each name that matches the User Defined Age
        #with default value of 0, and then we add the value from age2.
        listByAge[name] = listByAge.get(name,0)+age2

#Here we print the new dictionary with the users with User Defined Age.
print
print listByAge

.

#Results
Running: *\test.py (Thu Jun 06 05:10:02 2013)

Provide age: 19

amber 19
Garry 19

{'amber': 19, 'Garry': 19}

Execution Successful!

вы можете получить ключ с помощью dict.keys(),dict.values() и list.index() методы, см. примеры кода ниже:

names_dict = {'george':16,'amber':19}
search_age = int(raw_input("Provide age"))
key = names_dict.keys()[names_dict.values().index(search_age)]

Если вы хотите найти ключ по значению, вы можете использовать понимание словаря для создания словаря поиска, а затем использовать его для поиска ключа из значения.

lookup = {value: key for key, value in self.data}
lookup[value]

рассмотрите возможность использования панд. Как сказано в "Python для анализа данных" Уильяма Маккинни

другой способ думать о серии - это фиксированная длина, упорядоченная dict, поскольку это сопоставление значений индекса со значениями данных. Это может быть используется во многих контекстах, где вы можете использовать дикт.

import pandas as pd
list = {'george':16,'amber':19}
lookup_list = pd.Series(list)

для запроса серии выполните следующие действия:

lookup_list[lookup_list.values == 19]

что дает:

Out[1]: 
amber    19
dtype: int64

Если вам нужно сделать что-нибудь еще с выходных преобразование ответ в список может быть полезен:

answer = lookup_list[lookup_list.values == 19].index
answer = pd.Index.tolist(answer)

здесь recover_key принимает словарь и значение, чтобы найти в словаре. Затем мы перебираем ключи в словаре и делаем сравнение с тем, что имеет значение, и возвращаем этот конкретный ключ.

def recover_key(dicty,value):
    for a_key in dicty.keys():
        if (dicty[a_key] == value):
            return a_key
for name in mydict.keys():
    if mydict[name] == search_age:
        print name 
        #or do something else with it. 
        #if in a function append to a temporary list, 
        #then after the loop return the list

это ответ, но это может быть сделано с помощью фантазии "map / reduce", например:

def find_key(value, dictionary):
    return reduce(lambda x, y: x if x is not None else y,
                  map(lambda x: x[0] if x[1] == value else None, 
                      dictionary.iteritems()))
def get_Value(dic,value):
    for name in dic:
        if dic[name] == value:
            del dic[name]
            return name
get_key = lambda v, d: next(k for k in d if d[k] is v)

вот мой взгляд на это. Это хорошо для отображения нескольких результатов только в том случае, если вам нужен один. Так что я добавил список, а также

myList = {'george':16,'amber':19, 'rachel':19, 
           'david':15 }                         #Setting the dictionary
result=[]                                       #Making ready of the result list
search_age = int(input('Enter age '))

for keywords in myList.keys():
    if myList[keywords] ==search_age:
    result.append(keywords)                    #This part, we are making list of results

for res in result:                             #We are now printing the results
    print(res)

и это все...

нет простого способа найти ключ в списке, "глядя вверх" значение. Однако, если вы знаете значение, повторяя ключи, вы можете искать значения в словаре по элементу. Если D [элемент], где D-объект словаря, равен ключу, который вы пытаетесь найти, вы можете выполнить некоторый код.

D = {'Ali': 20, 'Marina': 12, 'George':16}
age = int(input('enter age:\t'))  
for element in D.keys():
    if D[element] == age:
        print(element)

Я знаю, что это старый, но вы можете довольно легко найти всех людей в списке с вашим возрастом поиска, используя понимание списка.

ages = {'george':16,'amber':19}
search = 16
print([name for (name, age) in ages.items() if age == search])

Я надеюсь, что это может помочь...

for key in list:
   if list[key] == search_value:
       return key

Я нашел это ответ очень эффективный, но все еще не очень легко читать для меня.

чтобы сделать его более ясным можно инвертировать ключ и значение из словаря. Это делает значения ключей и ключи значений, как видно здесь.

mydict = {'george':16,'amber':19}
res = dict((v,k) for k,v in mydict.iteritems())
print(res[16]) # Prints george

или

mydict = {'george':16,'amber':19}
dict((v,k) for k,v in mydict.iteritems())[16]

что по сути то же, что этот другой ответ.

попробуйте этот однострочный перевернуть словарь:

reversed_dictionary = dict(map(reversed, dictionary.items()))

Cat Plus Plus упомянул, что это не то, как словарь предназначен для использования. Вот почему:

определение словаря аналогично определению отображения в математике. В этом случае dict - это отображение K (набора ключей) в V (значения), но не наоборот. Если вы разыменуете dict, вы ожидаете получить ровно одно возвращенное значение. Но совершенно законно для разных ключей сопоставлять одно и то же значение, например:

d = { k1 : v1, k2 : v2, k3 : v1}

когда вы смотрите вверх a ключ по его соответствующему значению, вы по существу инвертируете словарь. Но отображение не обязательно обратимо! В этом примере запрос ключа, соответствующего v1, может привести к k1 или k3. Вы должны вернуть оба? Только первый найденный? Вот почему indexof () не определен для словарей.

Если вы знаете свои данные, вы можете сделать это. Но API не может предположить, что произвольный словарь обратим, следовательно, отсутствие такой операции.

иногда может потребоваться int ():

titleDic = {'Фильмы':1, 'Музыка':2}

def categoryTitleForNumber(self, num):
    search_title = ''
    for title, titleNum in self.titleDic.items():
        if int(titleNum) == int(num):
            search_title = title
    return search_title

вам нужно использовать словарь и обратный этого словаря. Значит вам нужна другая структура данных. Если вы находитесь в Python 3, Используйте enum модуль но если вы используете python 2.7 используйте enum34 обратно портирована в Python 2.

пример:

from enum import Enum

class Color(Enum): 
    red = 1 
    green = 2 
    blue = 3

>>> print(Color.red) 
Color.red

>>> print(repr(Color.red)) 
<color.red: 1=""> 

>>> type(Color.red) 
<enum 'color'=""> 
>>> isinstance(Color.green, Color) 
True 

>>> member = Color.red 
>>> member.name 
'red' 
>>> member.value 
1 

уже ответили, но так как несколько человек упомянули реверсирование словаря, вот как вы это делаете в одной строке (предполагая отображение 1:1) и некоторые различные данные perf:

python 2.6:

reversedict = dict([(value, key) for key, value in mydict.iteritems()])

2.7+:

reversedict = {value:key for key, value in mydict.iteritems()}

если вы думаете, что это не 1:1, вы все равно можете создать разумное обратное сопоставление с парой строк:

reversedict = defaultdict(list)
[reversedict[value].append(key) for key, value in mydict.iteritems()]

как медленно это: медленнее, чем простой поиск, но и не так медленно, как вы думаете - на 'прямой' 100000 entry dictionary, "быстрый" поиск (т. е. поиск значения, которое должно быть в начале ключей) был примерно в 10 раз быстрее, чем реверсирование всего словаря, а "медленный" поиск (ближе к концу) примерно в 4-5 раз быстрее. Так что после максимум около 10 поисков, он заплатил за себя.

вторая версия (со списками на элемент) занимает около 2,5 х до тех пор, как простая версия.

largedict = dict((x,x) for x in range(100000))

# Should be slow, has to search 90000 entries before it finds it
In [26]: %timeit largedict.keys()[largedict.values().index(90000)]
100 loops, best of 3: 4.81 ms per loop

# Should be fast, has to only search 9 entries to find it. 
In [27]: %timeit largedict.keys()[largedict.values().index(9)]
100 loops, best of 3: 2.94 ms per loop

# How about using iterkeys() instead of keys()?
# These are faster, because you don't have to create the entire keys array.
# You DO have to create the entire values array - more on that later.

In [31]: %timeit islice(largedict.iterkeys(), largedict.values().index(90000))
100 loops, best of 3: 3.38 ms per loop

In [32]: %timeit islice(largedict.iterkeys(), largedict.values().index(9))
1000 loops, best of 3: 1.48 ms per loop

In [24]: %timeit reversedict = dict([(value, key) for key, value in largedict.iteritems()])
10 loops, best of 3: 22.9 ms per loop

In [23]: %%timeit
....: reversedict = defaultdict(list)
....: [reversedict[value].append(key) for key, value in largedict.iteritems()]
....:
10 loops, best of 3: 53.6 ms per loop

также были некоторые интересные результаты с экспертом. Теоретически, фильтра должен быть быстрее, в этом мы можем использовать itervalues () и, возможно, не нужно создавать/проходить через весь список значений. На практике, результаты были... странный...

In [72]: %%timeit
....: myf = ifilter(lambda x: x[1] == 90000, largedict.iteritems())
....: myf.next()[0]
....:
100 loops, best of 3: 15.1 ms per loop

In [73]: %%timeit
....: myf = ifilter(lambda x: x[1] == 9, largedict.iteritems())
....: myf.next()[0]
....:
100000 loops, best of 3: 2.36 us per loop

таким образом, для небольших смещений он был значительно быстрее, чем любая предыдущая версия (2.36 *u*S против минимум 1.48 *m*S для предыдущих случаев). Однако, для больших смещений ближе к концу списка, он был значительно медленнее (15.1 МС и ту же 1.48 МС). Небольшая экономия на низком конце не стоит затрат на высоком конце, имхо.

d= {'george':16,'amber':19}

dict((v,k) for k,v in d.items()).get(16)

вывод выглядит следующим образом:

-> prints george

вот как вы получаете доступ к словарю, чтобы делать то, что вы хотите:

list = {'george': 16, 'amber': 19}
search_age = raw_input("Provide age")
for age in list:
    if list[age] == search_age:
        print age

конечно, ваши имена настолько выключены, что похоже, что он будет печатать возраст, но он печатает имя. Поскольку вы обращаетесь по имени, это становится более понятным, если вы пишете:

list = {'george': 16, 'amber': 19}
search_age = raw_input("Provide age")
for name in list:
    if list[name] == search_age:
        print name

еще лучше:

people = {'george': {'age': 16}, 'amber': {'age': 19}}
search_age = raw_input("Provide age")
for name in people:
    if people[name]['age'] == search_age:
        print name

вот решение, которое работает как в Python 2, так и в Python 3:

dict((v, k) for k, v in list.items())[search_age]

части до [search_age] создает обратный словарь (где значения ключей и наоборот). Вы можете создать вспомогательный метод, который будет кэшировать этот обратный словарь следующим образом:

def find_name(age, _rev_lookup=dict((v, k) for k, v in ages_by_name.items())):
    return _rev_lookup[age]

или даже в более общем случае фабрика, которая создаст метод поиска по возрасту для одного или нескольких из вас lists

def create_name_finder(ages_by_name):
    names_by_age = dict((v, k) for k, v in ages_by_name.items())
    def find_name(age):
      return names_by_age[age]

так что вы могли бы делать:

find_teen_by_age = create_name_finder({'george':16,'amber':19})
...
find_teen_by_age(search_age)

обратите внимание, что я переименовал list до ages_by_name так как первый является предопределенным типом.

dictionary = {'george' : 16, 'amber' : 19}
search_age = raw_input("Provide age")
key = [filter( lambda x: dictionary[x] == k  , dictionary ),[None]][0] 
# key = None from [None] which is a safeguard for not found.

для нескольких случаев применения:

keys = [filter( lambda x: dictionary[x] == k  , dictionary )]