2011-08-16 38 views
29

Tôi cần có thể tìm một mục trong một list (một mục trong trường hợp này là dict) dựa trên một số giá trị bên trong đó dict. Cấu trúc của list tôi cần phải xử lý như sau:Python: lấy một dict từ danh sách dựa trên một cái gì đó bên trong dict

[ 
    { 
     'title': 'some value', 
     'value': 123.4, 
     'id': 'an id' 
    }, 
    { 
     'title': 'another title', 
     'value': 567.8, 
     'id': 'another id' 
    }, 
    { 
     'title': 'last title', 
     'value': 901.2, 
     'id': 'yet another id' 
    } 
] 

Hãy cẩn thận:titlevalue thể được bất kỳ giá trị (và giống nhau), id sẽ là duy nhất.

Tôi cần có thể nhận được dict từ số list này dựa trên số id duy nhất. Tôi biết điều này có thể được thực hiện thông qua việc sử dụng vòng lặp, nhưng điều này có vẻ cồng kềnh, và tôi có cảm giác rằng có một phương pháp rõ ràng để làm điều này mà tôi không thấy nhờ não tan chảy.

Trả lời

54
my_item = next((item for item in my_list if item['id'] == my_unique_id), None) 

Điều này lặp qua danh sách cho đến khi nó tìm thấy mục đầu tiên khớp với my_unique_id, sau đó dừng lại. Nó không lưu trữ bất kỳ danh sách trung gian trong bộ nhớ (bằng cách sử dụng một biểu thức máy phát điện) hoặc yêu cầu một vòng lặp rõ ràng. Nó đặt my_item thành None không tìm thấy đối tượng nào. Đó là xấp xỉ như nhau như

for item in my_list: 
    if item['id'] == my_unique_id: 
     my_item = item 
     break 
else: 
    my_item = None 

else khoản trên for vòng được sử dụng khi các vòng lặp không kết thúc bằng một tuyên bố break.

+1

@agf Bạn khuyên bạn nên làm gì khi có nhiều kết quả phù hợp và bạn muốn trích xuất chúng trong danh sách (các dấu ngoặc kép phù hợp)? – Augiwan

+1

@UGS Nếu bạn cần quét toàn bộ danh sách và xây dựng danh sách kết quả và không chỉ tìm thấy kết quả đầu tiên, bạn không thể làm tốt hơn so với danh sách hiểu như '[mục cho mục trong my_list nếu mục ['id' ] == my_unique_id] '. – agf

0
In [2]: test_list 
Out[2]: 
[{'id': 'an id', 'title': 'some value', 'value': 123.40000000000001}, 
{'id': 'another id', 'title': 'another title', 'value': 567.79999999999995}, 
{'id': 'yet another id', 'title': 'last title', 'value': 901.20000000000005}] 

In [3]: [d for d in test_list if d["id"] == "an id"] 
Out[3]: [{'id': 'an id', 'title': 'some value', 'value': 123.40000000000001}] 

Sử dụng danh sách hiểu

+0

Điều này tiếp tục đi qua danh sách sau khi tìm thấy kết quả phù hợp. – agf

+0

Nếu ID nên là duy nhất, sau đó làm một len ​​() trên này sẽ cho thấy rằng bạn đang nhận được ID không độc đáo – TyrantWave

+0

Nó không phải là một vấn đề của id có thể là không duy nhất - đó là sự khác biệt giữa làm trung bình so sánh 'len (my_list)' hoặc 'len (my_list) // 2' so sánh. Phiên bản của bạn làm gấp đôi công việc (trung bình) là cần thiết. – agf

15

Nếu bạn phải làm điều này nhiều lần, bạn nên tạo một dictionnary lập chỉ mục bởi id với danh sách của bạn:

keys = [item['id'] for item in initial_list] 
new_dict = dict(zip(keys, initial_list)) 

>>>{ 
    'yet another id': {'id': 'yet another id', 'value': 901.20000000000005, 'title': 'last title'}, 
    'an id': {'id': 'an id', 'value': 123.40000000000001, 'title': 'some value'}, 
    'another id': {'id': 'another id', 'value': 567.79999999999995, 'title': 'another title'} 
} 

hoặc theo một cách one-liner như được đề xuất bởi agf:

new_dict = dict((item['id'], item) for item in initial_list) 
+2

'new_dict = dict ((mục ['id'], mục) cho mục trong initial_list)' ... tại sao tạo danh sách trung gian sau đó là 'zip'? – agf

0

Bạn có thể tạo một chức năng đơn giản cho mục đích này:

lVals = [{'title': 'some value', 'value': 123.4,'id': 'an id'}, 
{'title': 'another title', 'value': 567.8,'id': 'another id'}, 
{'title': 'last title', 'value': 901.2, 'id': 'yet another id'}] 

def get_by_id(vals, expId): return next(x for x in vals if x['id'] == expId) 

get_by_id(lVals, 'an id') 
>>> {'value': 123.4, 'title': 'some value', 'id': 'an id'} 
Các vấn đề liên quan