Câu trả lời:
Bạn có thể sử dụng biểu thức trình tạo :
>>> 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'}
Nếu bạn cần xử lý mục không có ở đó, thì bạn có thể thực hiện những gì người dùng Matt đề xuất trong nhận xét của mình và cung cấp mặc định bằng API hơi khác:
next((item for item in dicts if item["name"] == "Pam"), None)
Và để tìm chỉ mục của mục, thay vì chính mục đó, bạn có thể liệt kê () danh sách:
next((i for i, item in enumerate(dicts) if item["name"] == "Pam"), None)
[item for item in dicts if item["name"] == "Pam"][0]
?
enumerate()
để tạo chỉ mục đang chạy : next(i for i, item in enumerate(dicts) if item["name"] == "Pam")
.
Điều này đối với tôi theo cách pythonic nhất:
people = [
{'name': "Tom", 'age': 10},
{'name': "Mark", 'age': 5},
{'name': "Pam", 'age': 7}
]
filter(lambda person: person['name'] == 'Pam', people)
kết quả (được trả về dưới dạng danh sách trong Python 2):
[{'age': 7, 'name': 'Pam'}]
Lưu ý: Trong Python 3, một đối tượng bộ lọc được trả về. Vì vậy, giải pháp python3 sẽ là:
list(filter(lambda person: person['name'] == 'Pam', people))
len()
, bạn cần phải gọi list()
kết quả trước. Hoặc: stackoverflow.com/questions/19182188/ khăn
r
alist
next(filter(lambda x: x['name'] == 'Pam', dicts))
Câu trả lời của @ Frédéric Hamidi là tuyệt vời. Trong Python 3.x cú pháp .next()
thay đổi một chút. Do đó, một sửa đổi nhỏ:
>>> 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'}
Như được đề cập trong các nhận xét của @Matt, bạn có thể thêm một giá trị mặc định như sau:
>>> 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
>>>
Bạn có thể sử dụng một danh sách hiểu :
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
Tôi đã thử nghiệm các phương pháp khác nhau để đi qua một danh sách các từ điển và trả về các từ điển trong đó khóa x có một giá trị nhất định.
Các kết quả:
Tất cả các thử nghiệm được thực hiện với 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
Các kết quả:
1.7303 # normal list iteration
1.3849 # generator expression
1.3158 # list comprehension
7.7848 # filter
Để thêm một chút xíu vào @ Frédéricoutidi.
Trong trường hợp bạn không chắc chắn một khóa nằm trong danh sách các ký hiệu, một cái gì đó như thế này sẽ giúp ích:
next((item for item in dicts if item.get("name") and item["name"] == "Pam"), None)
item.get("name") == "Pam"
Bạn đã bao giờ thử gói gấu trúc chưa? Nó hoàn hảo cho loại nhiệm vụ tìm kiếm này và cũng được tối ưu hóa.
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) ]
Tôi đã thêm một chút điểm chuẩn bên dưới để minh họa thời gian chạy nhanh hơn của gấu trúc ở quy mô lớn hơn, tức là 100k + mục:
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]
Đây là một cách ...
Bạn có thể đạt được điều này với việc sử dụng bộ lọc và các phương thức tiếp theo trong Python.
filter
phương thức lọc chuỗi đã cho và trả về một iterator.
next
phương thức chấp nhận một trình vòng lặp và trả về phần tử tiếp theo trong danh sách.
Vì vậy, bạn có thể tìm thấy các yếu tố bằng cách,
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)
và đầu ra là,
{'name': 'Pam', 'age': 7}
Lưu ý: Mã ở trên sẽ trả về None
incase nếu không tìm thấy tên chúng tôi đang tìm kiếm.
Suy nghĩ đầu tiên của tôi là bạn có thể muốn xem xét việc tạo ra một từ điển của những từ điển này ... ví dụ, nếu bạn sẽ tìm kiếm nó nhiều hơn một số lần nhỏ.
Tuy nhiên đó có thể là một tối ưu hóa sớm. Điều gì sẽ sai với:
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'}
Bạn có thể thử điều này:
''' 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'}
Dưới đây là so sánh bằng cách sử dụng danh sách lặp lại, sử dụng bộ lọc + lambda hoặc tái cấu trúc (nếu cần hoặc hợp lệ với trường hợp của bạn) mã của bạn để ra lệnh cho các lệnh thay vì danh sách các lệnh
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
Và đầu ra là thế này:
List of Dictionries took: 0.0099310874939
Using filter took: 0.0121960639954
Dict of Dicts took: 4.05311584473e-06
Kết luận: Rõ ràng có một từ điển các dicts là cách hiệu quả nhất để có thể tìm kiếm trong những trường hợp đó, nơi bạn biết rằng bạn sẽ chỉ tìm kiếm theo id. thú vị khi sử dụng bộ lọc là giải pháp chậm nhất.
Bạn phải đi qua tất cả các yếu tố của danh sách. Không có lối tắt!
Trừ khi ở một nơi khác, bạn giữ một từ điển các tên chỉ vào các mục của danh sách, nhưng sau đó bạn phải quan tâm đến hậu quả của việc bật một phần tử từ danh sách của bạn.
Tôi tìm thấy chủ đề này khi tôi đang tìm kiếm một câu trả lời cho cùng một câu hỏi. Trong khi tôi nhận ra rằng đó là một câu trả lời muộn, tôi nghĩ tôi sẽ đóng góp nó trong trường hợp nó hữu ích cho bất kỳ ai khác:
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
Hầu hết (nếu không phải tất cả) các triển khai được đề xuất ở đây có hai lỗ hổng:
Một đề xuất cập nhật:
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)
Có thể không phải là pythonic nhất, nhưng ít nhất một chút không an toàn.
Sử dụng:
>>> 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}
Các ý chính .