Antworten:
Sie können einen Generatorausdruck verwenden :
>>> 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'}
Wenn Sie das Element behandeln müssen, das nicht vorhanden ist, können Sie das tun, was Benutzer Matt in seinem Kommentar vorgeschlagen hat, und einen Standard mithilfe einer etwas anderen API angeben:
next((item for item in dicts if item["name"] == "Pam"), None)
Und um den Index des Elements und nicht das Element selbst zu finden, können Sie die Liste auflisten () :
next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)
[item for item in dicts if item["name"] == "Pam"][0]
?
enumerate()
, um einen laufenden Index zu generieren : next(i for i, item in enumerate(dicts) if item["name"] == "Pam")
.
Das sieht für mich am pythonischsten aus:
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
filter(lambda person: person['name'] == 'Pam', people)
Ergebnis (in Python 2 als Liste zurückgegeben):
[{'age': 7, 'name': 'Pam'}]
Hinweis: In Python 3 wird ein Filterobjekt zurückgegeben. Die Python3-Lösung wäre also:
list(filter(lambda person: person['name'] == 'Pam', people))
len()
, müssen Sie list()
zuerst das Ergebnis aufrufen . Oder: stackoverflow.com/questions/19182188/…
r
ist einlist
next(filter(lambda x: x['name'] == 'Pam', dicts))
@ Frédéric Hamidis Antwort ist großartig. In Python 3.x hat sich die Syntax für .next()
leicht geändert. Also eine kleine Modifikation:
>>> 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'}
Wie in den Kommentaren von @Matt erwähnt, können Sie einen Standardwert als solchen hinzufügen:
>>> 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
>>>
Sie können ein Listenverständnis verwenden :
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
Ich habe verschiedene Methoden getestet, um eine Liste von Wörterbüchern durchzugehen und die Wörterbücher zurückzugeben, in denen Schlüssel x einen bestimmten Wert hat.
Ergebnisse:
Alle Tests wurden mit Python 3.6 .4, W7x64 durchgeführt.
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
Ergebnisse:
1.7303 # normal list iteration
1.3849 # generator expression
1.3158 # list comprehension
7.7848 # filter
Um @ FrédéricHamidi nur ein kleines bisschen hinzuzufügen.
Falls Sie nicht sicher sind, ob ein Schlüssel in der Liste der Diktate enthalten ist, hilft Folgendes:
next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
item.get("name") == "Pam"
Haben Sie jemals das Pandas-Paket ausprobiert? Es ist perfekt für diese Art von Suchaufgabe und auch optimiert.
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) ]
Ich habe unten ein wenig Benchmarking hinzugefügt, um die schnelleren Laufzeiten der Pandas in größerem Maßstab zu veranschaulichen, dh mehr als 100.000 Einträge:
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]
Dies ist eine Möglichkeit ...
Sie können dies mit der Verwendung von Filter und nächsten Methoden in Python erreichen.
filter
Methode filtert die angegebene Sequenz und gibt einen Iterator zurück.
next
Die Methode akzeptiert einen Iterator und gibt das nächste Element in der Liste zurück.
So können Sie das Element finden durch,
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)
und die Ausgabe ist,
{'name': 'Pam', 'age': 7}
Hinweis: Der obige Code wird zurückgegeben, None
falls der gesuchte Name nicht gefunden wird.
Mein erster Gedanke wäre, dass Sie vielleicht ein Wörterbuch dieser Wörterbücher erstellen möchten ... wenn Sie es beispielsweise mehr als selten durchsuchen würden.
Dies könnte jedoch eine vorzeitige Optimierung sein. Was wäre falsch daran:
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'}
Sie können dies versuchen:
''' 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'}
Hier ist ein Vergleich mit iterierender Throuhg-Liste, Filter + Lambda oder Refactoring (falls erforderlich oder für Ihren Fall gültig) Ihres Codes, um Diktate anstelle von Diktaten zu diktieren
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
Und die Ausgabe ist folgende:
List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06
Fazit: Ein Wörterbuch mit Diktaten ist eindeutig die effizienteste Methode, um in den Fällen suchen zu können, in denen Sie wissen, dass Sie nur nach IDs suchen. Interessanterweise ist die Verwendung von Filtern die langsamste Lösung.
Sie müssen alle Elemente der Liste durchgehen. Es gibt keine Abkürzung!
Sofern Sie nicht irgendwo anders ein Wörterbuch mit den Namen führen, die auf die Elemente der Liste verweisen, müssen Sie sich dann um die Konsequenzen kümmern, wenn Sie ein Element aus Ihrer Liste entfernen.
Ich habe diesen Thread gefunden, als ich nach einer Antwort auf dieselbe Frage gesucht habe. Obwohl mir klar ist, dass es eine späte Antwort ist, dachte ich, ich würde sie beitragen, falls es für andere nützlich ist:
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
Die meisten (wenn nicht alle) hier vorgeschlagenen Implementierungen weisen zwei Mängel auf:
Ein aktualisierter Vorschlag:
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)
Vielleicht nicht die pythonischste, aber zumindest ein bisschen ausfallsicherer.
Verwendungszweck:
>>> 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}
Das Wesentliche .