Odpowiedzi:
Możesz użyć wyrażenia generatora :
>>> 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'}
Jeśli musisz poradzić sobie z brakiem elementu, możesz zrobić to, co sugerował użytkownik Matt w swoim komentarzu, i podać wartość domyślną, używając nieco innego interfejsu API:
next((item for item in dicts if item["name"] == "Pam"), None)
Aby znaleźć indeks elementu, a nie sam element, możesz wyliczyć () listę:
next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)
[item for item in dicts if item["name"] == "Pam"][0]
?
enumerate()
do wygenerowania systemem indeksu: next(i for i, item in enumerate(dicts) if item["name"] == "Pam")
.
To wydaje mi się najbardziej pytonicznym sposobem:
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
filter(lambda person: person['name'] == 'Pam', people)
wynik (zwrócony jako lista w Pythonie 2):
[{'age': 7, 'name': 'Pam'}]
Uwaga: W Pythonie 3 zwracany jest obiekt filtru. Tak więc rozwiązaniem python3 byłoby:
list(filter(lambda person: person['name'] == 'Pam', people))
len()
, musisz najpierw wywołać list()
wynik. Lub: stackoverflow.com/questions/19182188/…
r
jestlist
next(filter(lambda x: x['name'] == 'Pam', dicts))
@ Frédéric Hamidi odpowiedź jest świetna. W Pythonie 3.x .next()
zmieniono nieco składnię . Zatem niewielka modyfikacja:
>>> 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'}
Jak wspomniano w komentarzach @Matt, możesz dodać wartość domyślną jako taką:
>>> 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
>>>
Możesz użyć rozumienia listy :
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
Przetestowałem różne metody przeglądania listy słowników i zwracania słowników, w których klucz x ma określoną wartość.
Wyniki:
Wszystkie testy wykonane w Pythonie 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
Wyniki:
1.7303 # normal list iteration
1.3849 # generator expression
1.3158 # list comprehension
7.7848 # filter
Aby dodać tylko odrobinę do @ FrédéricHamidi.
Jeśli nie masz pewności, czy klucz znajduje się na liście nagrań, coś takiego pomoże:
next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
item.get("name") == "Pam"
Czy kiedykolwiek wypróbowałeś pakiet pand? Jest idealny do tego rodzaju zadań wyszukiwania i zoptymalizowany.
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) ]
Poniżej dodałem trochę testów porównawczych, aby zilustrować szybsze czasy działania pand na większą skalę, tj. Ponad 100 000 wpisów:
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]
To jest jeden sposób ...
Możesz to osiągnąć za pomocą filtru i następnych metod w Pythonie.
filter
Metoda filtruje podaną sekwencję i zwraca iterator.
next
Metoda akceptuje iterator i zwraca następny element na liście.
Aby znaleźć element,
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)
a wynikiem jest
{'name': 'Pam', 'age': 7}
Uwaga: powyższy kod zwróci None
incase, jeśli szukana nazwa nie zostanie znaleziona.
Najpierw pomyślałem, że możesz rozważyć utworzenie słownika tych słowników ... jeśli, na przykład, będziesz go przeszukiwał więcej niż kilka razy.
Może to być jednak przedwczesna optymalizacja. Co byłoby nie tak z:
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'}
Możesz spróbować:
''' 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'}
Oto porównanie za pomocą iteracji listy, za pomocą filtru + lambda lub refaktoryzacji (jeśli jest to konieczne lub ważne w przypadku) kodu do dyktowania, a nie listy dykt
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
A wynik jest następujący:
List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06
Wniosek: Najwyraźniej posiadanie słownika słowników jest najskuteczniejszym sposobem wyszukiwania w tych przypadkach, w których wiesz, że będziesz wyszukiwał tylko według identyfikatora. co ciekawe, użycie filtru jest najwolniejszym rozwiązaniem.
Musisz przejść przez wszystkie elementy listy. Nie ma skrótu!
Chyba że gdzieś indziej prowadzisz słownik nazw wskazujący na elementy listy, ale musisz zadbać o konsekwencje wyskakiwania elementu z listy.
Znalazłem ten wątek, gdy szukałem odpowiedzi na to samo pytanie. Chociaż zdaję sobie sprawę, że jest to późna odpowiedź, pomyślałem, że wrócę na wypadek, gdyby była przydatna dla kogoś innego:
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
Większość (jeśli nie wszystkie) implementacje tutaj zaproponowane mają dwie wady:
Zaktualizowana propozycja:
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)
Może nie najbardziej pythoniczny, ale przynajmniej nieco bardziej bezpieczny w razie awarii.
Stosowanie:
>>> 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}
Sens .