Ответы:
Вы можете использовать выражение генератора :
>>> dicts = [
... { "name": "Tom", "age": 10 },
... { "name": "Mark", "age": 5 },
... { "name": "Pam", "age": 7 },
... { "name": "Dick", "age": 12 }
... ]
>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}
Если вам нужно обработать элемент, которого там нет, то вы можете сделать то, что предложил пользователь Мэтт в своем комментарии, и задать значение по умолчанию, используя немного другой API:
next((item for item in dicts if item["name"] == "Pam"), None)
И чтобы найти индекс элемента, а не сам элемент, вы можете перечислить () список:
next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)
[item for item in dicts if item["name"] == "Pam"][0]
?
enumerate()
генерировать бегущий индекс: next(i for i, item in enumerate(dicts) if item["name"] == "Pam")
.
Это выглядит для меня наиболее питоническим образом:
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
filter(lambda person: person['name'] == 'Pam', people)
результат (возвращается в виде списка в Python 2):
[{'age': 7, 'name': 'Pam'}]
Примечание. В Python 3 возвращается объект фильтра. Таким образом, решение python3 будет:
list(filter(lambda person: person['name'] == 'Pam', people))
len()
, вам нужно list()
сначала вызвать результат. Или: stackoverflow.com/questions/19182188/…
r
же как иlist
next(filter(lambda x: x['name'] == 'Pam', dicts))
@ Ответ Фредерика Хамиди отличный. В Python 3.x синтаксис для .next()
слегка изменился. Таким образом, небольшое изменение:
>>> dicts = [
{ "name": "Tom", "age": 10 },
{ "name": "Mark", "age": 5 },
{ "name": "Pam", "age": 7 },
{ "name": "Dick", "age": 12 }
]
>>> next(item for item in dicts if item["name"] == "Pam")
{'age': 7, 'name': 'Pam'}
Как упоминалось в комментариях @Matt, вы можете добавить значение по умолчанию как таковое:
>>> next((item for item in dicts if item["name"] == "Pam"), False)
{'name': 'Pam', 'age': 7}
>>> next((item for item in dicts if item["name"] == "Sam"), False)
False
>>>
Вы можете использовать понимание списка :
def search(name, people):
return [element for element in people if element['name'] == name]
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
def search(name):
for p in people:
if p['name'] == name:
return p
search("Pam")
def search(list, key, value): for item in list: if item[key] == value: return item
Я протестировал различные методы, чтобы просмотреть список словарей и вернуть словари, в которых ключ x имеет определенное значение.
Результаты:
Все тесты выполнены на Python 3.6 .4, W7x64.
from random import randint
from timeit import timeit
list_dicts = []
for _ in range(1000): # number of dicts in the list
dict_tmp = {}
for i in range(10): # number of keys for each dict
dict_tmp[f"key{i}"] = randint(0,50)
list_dicts.append( dict_tmp )
def a():
# normal iteration over all elements
for dict_ in list_dicts:
if dict_["key3"] == 20:
pass
def b():
# use 'generator'
for dict_ in (x for x in list_dicts if x["key3"] == 20):
pass
def c():
# use 'list'
for dict_ in [x for x in list_dicts if x["key3"] == 20]:
pass
def d():
# use 'filter'
for dict_ in filter(lambda x: x['key3'] == 20, list_dicts):
pass
Результаты:
1.7303 # normal list iteration
1.3849 # generator expression
1.3158 # list comprehension
7.7848 # filter
Чтобы добавить чуть-чуть к @ FrédéricHamidi.
Если вы не уверены, что ключ находится в списке диктов, может помочь что-то вроде этого:
next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
item.get("name") == "Pam"
Вы когда-нибудь пробовали пакет с пандами? Он идеально подходит для такой задачи поиска и оптимизирован.
import pandas as pd
listOfDicts = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
# Create a data frame, keys are used as column headers.
# Dict items with the same key are entered into the same respective column.
df = pd.DataFrame(listOfDicts)
# The pandas dataframe allows you to pick out specific values like so:
df2 = df[ (df['name'] == 'Pam') & (df['age'] == 7) ]
# Alternate syntax, same thing
df2 = df[ (df.name == 'Pam') & (df.age == 7) ]
Ниже я добавил немного бенчмаркинга, чтобы проиллюстрировать более быстрое время выполнения панд в более широком масштабе, т.е. 100 000 записей:
setup_large = 'dicts = [];\
[dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 })) for _ in range(25000)];\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);'
setup_small = 'dicts = [];\
dicts.extend(({ "name": "Tom", "age": 10 },{ "name": "Mark", "age": 5 },\
{ "name": "Pam", "age": 7 },{ "name": "Dick", "age": 12 }));\
from operator import itemgetter;import pandas as pd;\
df = pd.DataFrame(dicts);'
method1 = '[item for item in dicts if item["name"] == "Pam"]'
method2 = 'df[df["name"] == "Pam"]'
import timeit
t = timeit.Timer(method1, setup_small)
print('Small Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_small)
print('Small Method Pandas: ' + str(t.timeit(100)))
t = timeit.Timer(method1, setup_large)
print('Large Method LC: ' + str(t.timeit(100)))
t = timeit.Timer(method2, setup_large)
print('Large Method Pandas: ' + str(t.timeit(100)))
#Small Method LC: 0.000191926956177
#Small Method Pandas: 0.044392824173
#Large Method LC: 1.98827004433
#Large Method Pandas: 0.324505090714
names = [{'name':'Tom', 'age': 10}, {'name': 'Mark', 'age': 5}, {'name': 'Pam', 'age': 7}]
resultlist = [d for d in names if d.get('name', '') == 'Pam']
first_result = resultlist[0]
Это один из способов ...
Вы можете достичь этого с помощью фильтра и следующих методов в Python.
filter
Метод фильтрует заданную последовательность и возвращает итератор.
next
Метод принимает итератор и возвращает следующий элемент в списке.
Таким образом, вы можете найти элемент,
my_dict = [
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
next(filter(lambda obj: obj.get('name') == 'Pam', my_dict), None)
и вывод есть,
{'name': 'Pam', 'age': 7}
Примечание. Приведенный выше код вернет None
incase, если имя, которое мы ищем, не найдено.
Моей первой мыслью было бы, что вы, возможно, захотите создать словарь этих словарей ... если, например, вы собираетесь искать его более чем несколько раз.
Однако это может быть преждевременной оптимизацией. Что было бы не так с:
def get_records(key, store=dict()):
'''Return a list of all records containing name==key from our store
'''
assert key is not None
return [d for d in store if d['name']==key]
dicts=[
{"name": "Tom", "age": 10},
{"name": "Mark", "age": 5},
{"name": "Pam", "age": 7}
]
from collections import defaultdict
dicts_by_name=defaultdict(list)
for d in dicts:
dicts_by_name[d['name']]=d
print dicts_by_name['Tom']
#output
#>>>
#{'age': 10, 'name': 'Tom'}
Вы можете попробовать это:
''' lst: list of dictionaries '''
lst = [{"name": "Tom", "age": 10}, {"name": "Mark", "age": 5}, {"name": "Pam", "age": 7}]
search = raw_input("What name: ") #Input name that needs to be searched (say 'Pam')
print [ lst[i] for i in range(len(lst)) if(lst[i]["name"]==search) ][0] #Output
>>> {'age': 7, 'name': 'Pam'}
Вот сравнение с использованием итерации по списку, с использованием filter + lambda или рефакторингом (если необходимо или допустимо для вашего случая) вашего кода, чтобы диктовать его, а не список.
import time
# Build list of dicts
list_of_dicts = list()
for i in range(100000):
list_of_dicts.append({'id': i, 'name': 'Tom'})
# Build dict of dicts
dict_of_dicts = dict()
for i in range(100000):
dict_of_dicts[i] = {'name': 'Tom'}
# Find the one with ID of 99
# 1. iterate through the list
lod_ts = time.time()
for elem in list_of_dicts:
if elem['id'] == 99999:
break
lod_tf = time.time()
lod_td = lod_tf - lod_ts
# 2. Use filter
f_ts = time.time()
x = filter(lambda k: k['id'] == 99999, list_of_dicts)
f_tf = time.time()
f_td = f_tf- f_ts
# 3. find it in dict of dicts
dod_ts = time.time()
x = dict_of_dicts[99999]
dod_tf = time.time()
dod_td = dod_tf - dod_ts
print 'List of Dictionries took: %s' % lod_td
print 'Using filter took: %s' % f_td
print 'Dict of Dicts took: %s' % dod_td
И вывод такой:
List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06
Вывод: очевидно, что наличие словаря словарей является наиболее эффективным способом поиска в тех случаях, когда, как вы знаете, вы будете искать только по идентификаторам. Интересно, что использование фильтра является самым медленным решением.
Большинство (если не все) реализации, предложенные здесь, имеют два недостатка:
Обновленное предложение:
def find_first_in_list(objects, **kwargs):
return next((obj for obj in objects if
len(set(obj.keys()).intersection(kwargs.keys())) > 0 and
all([obj[k] == v for k, v in kwargs.items() if k in obj.keys()])),
None)
Возможно, не самый питонический, но, по крайней мере, немного более безопасный.
Применение:
>>> obj1 = find_first_in_list(list_of_dict, name='Pam', age=7)
>>> obj2 = find_first_in_list(list_of_dict, name='Pam', age=27)
>>> obj3 = find_first_in_list(list_of_dict, name='Pam', address='nowhere')
>>>
>>> print(obj1, obj2, obj3)
{"name": "Pam", "age": 7}, None, {"name": "Pam", "age": 7}
Суть .
Вы должны пройти через все элементы списка. Там нет ярлыка!
Если где-то еще вы не ведете словарь имен, указывающих на элементы списка, но тогда вы должны позаботиться о последствиях выталкивания элемента из вашего списка.
Я нашел эту ветку, когда искал ответ на тот же вопрос. Хотя я понимаю, что это поздний ответ, я решил внести его, если он пригодится кому-то еще:
def find_dict_in_list(dicts, default=None, **kwargs):
"""Find first matching :obj:`dict` in :obj:`list`.
:param list dicts: List of dictionaries.
:param dict default: Optional. Default dictionary to return.
Defaults to `None`.
:param **kwargs: `key=value` pairs to match in :obj:`dict`.
:returns: First matching :obj:`dict` from `dicts`.
:rtype: dict
"""
rval = default
for d in dicts:
is_found = False
# Search for keys in dict.
for k, v in kwargs.items():
if d.get(k, None) == v:
is_found = True
else:
is_found = False
break
if is_found:
rval = d
break
return rval
if __name__ == '__main__':
# Tests
dicts = []
keys = 'spam eggs shrubbery knight'.split()
start = 0
for _ in range(4):
dct = {k: v for k, v in zip(keys, range(start, start+4))}
dicts.append(dct)
start += 4
# Find each dict based on 'spam' key only.
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam) == dicts[x]
# Find each dict based on 'spam' and 'shrubbery' keys.
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+2) == dicts[x]
# Search for one correct key, one incorrect key:
for x in range(len(dicts)):
spam = x*4
assert find_dict_in_list(dicts, spam=spam, shrubbery=spam+1) is None
# Search for non-existent dict.
for x in range(len(dicts)):
spam = x+100
assert find_dict_in_list(dicts, spam=spam) is None