2012-06-18 43 views
80

Hãy nói rằng tôi có một danh sách các từ điển:Python - Danh sách các từ điển độc đáo

[ 
    {'id': 1, 'name': 'john', 'age': 34}, 
    {'id': 1, 'name': 'john', 'age': 34}, 
    {'id': 2, 'name': 'hanna', 'age': 30}, 
] 

và tôi cần để có được một danh sách các từ điển duy nhất (loại bỏ các bản sao):

[ 
    {'id': 1, 'name': 'john', 'age': 34}, 
    {'id': 2, 'name': 'hanna', 'age': 30}, 
] 

Can ai giúp tôi với cách hiệu quả nhất để đạt được điều này trong Python?

+3

Làm thế nào rộng là các bộ từ điển? Bạn có cần kiểm tra thuộc tính riêng lẻ để xác định các bản sao hay đang kiểm tra một giá trị duy nhất trong chúng đủ không? –

+0

Những dicts có 8 cặp khóa: giá trị và danh sách có 200 dicts.Họ thực sự có một ID và nó an toàn cho tôi để loại bỏ các dict từ danh sách nếu giá trị ID tìm thấy là một bản sao. – Limaaf

+0

Bản sao có thể có của [Cách tạo giá trị trong danh sách từ điển độc đáo?] (Http://stackoverflow.com/questions/31792680/how-to-make-values-in-list-of-dictionary-unique) – Abhijeet

Trả lời

123

Vì vậy, hãy tạo một dict tạm thời bằng khóa là id. Điều này lọc ra các bản sao. Các values() của dict sẽ là danh sách

Trong Python2.7

>>> L=[ 
... {'id':1,'name':'john', 'age':34}, 
... {'id':1,'name':'john', 'age':34}, 
... {'id':2,'name':'hanna', 'age':30}, 
... ] 
>>> {v['id']:v for v in L}.values() 
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}] 

Trong Python3

>>> L=[ 
... {'id':1,'name':'john', 'age':34}, 
... {'id':1,'name':'john', 'age':34}, 
... {'id':2,'name':'hanna', 'age':30}, 
... ] 
>>> list({v['id']:v for v in L}.values()) 
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}] 

Trong python2.5/2,6

>>> L=[ 
... {'id':1,'name':'john', 'age':34}, 
... {'id':1,'name':'john', 'age':34}, 
... {'id':2,'name':'hanna', 'age':30}, 
... ] 
>>> dict((v['id'],v) for v in L).values() 
[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}] 
+0

Điều này đã làm công việc =) Khá nhiều tất cả các câu trả lời là chính xác và giúp tôi ra . Đánh dấu điều này là chính xác cho việc bao gồm các mã phiên bản python khác. Chúc mừng mọi người đã giúp tôi. – Limaaf

+0

@John La Rooy - cách người dùng có thể sử dụng tương tự để xóa từ điển khỏi danh sách dựa trên nhiều thuộc tính, đã thử cách này nhưng có vẻ không hoạt động> {v ['flight'] ['lon'] ['lat']: v cho v trong dòng} .values ​​() –

+0

@JorgeVidinha giả sử mỗi có thể được đúc thành str (hoặc unicode), hãy thử điều này: '{str (v ['flight']) + ':' + str (v ['lon' ]) + ',' + str (v ['lat']): v cho v trong dòng} .values ​​() 'Điều này chỉ tạo ra một khóa duy nhất dựa trên giá trị của bạn. Giống như '' MH370: -21.474370,86.325589'' – whunterknight

0

Đây là giải pháp tôi tìm thấy:

usedID = [] 

x = [ 
{'id':1,'name':'john', 'age':34}, 
{'id':1,'name':'john', 'age':34}, 
{'id':2,'name':'hanna', 'age':30}, 
] 

for each in x: 
    if each['id'] in usedID: 
     x.remove(each) 
    else: 
     usedID.append(each['id']) 

print x 

Về cơ bản bạn kiểm tra xem ID có mặt trong danh sách, nếu có, xóa các từ điển, nếu không muốn nói, thêm ID vào danh sách

+0

Tôi muốn sử dụng một tập hợp thay vì danh sách cho usedID. Đó là một tra cứu nhanh hơn, và dễ đọc hơn – happydave

+0

Yea tôi không biết về bộ ... nhưng tôi đang học ... Tôi chỉ nhìn vào câu trả lời @gnibbler ... – tabchas

+0

Bạn cần phải kiểm tra điều này nhiều hơn một chút. Sửa đổi danh sách trong khi bạn đang lặp qua nó có thể không phải luôn luôn làm việc như bạn mong đợi –

49

Cách thông thường để tìm kiếm chỉ là yếu tố chung trong một bộ là sử dụng lớp set của Python. Chỉ cần thêm tất cả các phần tử vào tập hợp, sau đó chuyển đổi tập hợp thành list và bam các bản sao đã biến mất.

Vấn đề, tất nhiên, là set() chỉ có thể chứa các mục nhập có thể băm, và dict không thể băm.

Nếu tôi có vấn đề này, giải pháp của tôi sẽ được chuyển đổi mỗi dict thành một chuỗi đại diện cho dict, sau đó thêm tất cả các chuỗi đến một set() sau đó đọc ra các giá trị chuỗi như một list() và chuyển đổi trở lại dict.

Biểu diễn tốt của một dạng dict ở dạng chuỗi là định dạng JSON. Và Python có một mô-đun tích hợp cho JSON (được gọi là json tất nhiên).

Vấn đề còn lại là các phần tử trong dict không được đặt hàng và khi Python chuyển đổi dict thành chuỗi JSON, bạn có thể nhận được hai chuỗi JSON đại diện cho các từ điển tương đương nhưng không phải là chuỗi giống hệt nhau. Giải pháp dễ dàng là vượt qua đối số sort_keys=True khi bạn gọi json.dumps().

EDIT: Giải pháp này giả định rằng một dict nhất định có thể có bất kỳ phần nào khác nhau. Nếu chúng ta có thể giả định rằng mỗi dict có cùng giá trị "id" sẽ khớp với nhau dict với cùng giá trị "id", thì điều này là quá mức cần thiết; Giải pháp của @ gnibbler sẽ nhanh hơn và dễ dàng hơn.

EDIT: Bây giờ có một nhận xét từ André Lima một cách rõ ràng nói rằng nếu ID là một bản sao, nó an toàn để giả định rằng toàn bộ dict là một bản sao.Vì vậy, câu trả lời này là quá mức cần thiết và tôi khuyên bạn nên @ gnibbler của câu trả lời.

+0

Cảm ơn sự giúp đỡ của steveha. Câu trả lời của bạn thực sự đã cho tôi một số kiến ​​thức mà tôi không có, kể từ khi tôi bắt đầu với Python =) – Limaaf

+1

Trong khi quá mức cần thiết cho ID trong trường hợp cụ thể này, đây vẫn là một câu trả lời tuyệt vời! –

+4

Điều này giúp tôi vì từ điển của tôi không có khóa và chỉ được nhận dạng duy nhất bởi tất cả các mục nhập của nó. Cảm ơn! – ericso

7

Vì số id là đủ để phát hiện trùng lặp và id có thể băm: chạy 'em qua từ điển có khóa là id làm khóa. Giá trị cho mỗi khóa là từ điển gốc.

deduped_dicts = dict((item["id"], item) for item in list_of_dicts).values() 

Trong Python 3, values() không trả về danh sách; bạn sẽ cần phải bọc toàn bộ cánh tay phải phía biểu hiện rằng trong list(), và bạn có thể viết thịt của biểu thức về kinh tế hơn là một sự hiểu biết dict:

deduped_dicts = list({item["id"]: item for item in list_of_dicts}.values()) 

Lưu ý rằng kết quả có thể sẽ không được theo thứ tự như bản gốc. Nếu đó là yêu cầu, bạn có thể sử dụng số Collections.OrderedDict thay vì số dict.

Là một sang một bên, nó có thể làm cho một ý thức tốt để chỉ giữ dữ liệu trong từ điển sử dụng id làm khóa để bắt đầu.

9

Dưới đây là một giải pháp nhỏ gọn hợp lý, mặc dù tôi nghi ngờ không đặc biệt hiệu quả (để đặt nó nhẹ):

>>> ds = [{'id':1,'name':'john', 'age':34}, 
...  {'id':1,'name':'john', 'age':34}, 
...  {'id':2,'name':'hanna', 'age':30} 
...  ] 
>>> map(dict, set(tuple(sorted(d.items())) for d in ds)) 
[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}] 
+1

Bao quanh lệnh gọi 'map()' với 'list()' trong Python 3 để lấy lại danh sách, nếu không nó là đối tượng 'map'. – dmn

0

tùy chọn Khá đơn giản:

L = [ 
    {'id':1,'name':'john', 'age':34}, 
    {'id':1,'name':'john', 'age':34}, 
    {'id':2,'name':'hanna', 'age':30}, 
    ] 


D = dict() 
for l in L: D[l['id']] = l 
output = list(D.values()) 
print output 
0

Heres một thực hiện với rất ít chi phí bộ nhớ tại chi phí không nhỏ gọn như phần còn lại.

values = [ {'id':2,'name':'hanna', 'age':30}, 
      {'id':1,'name':'john', 'age':34}, 
      {'id':1,'name':'john', 'age':34}, 
      {'id':2,'name':'hanna', 'age':30}, 
      {'id':1,'name':'john', 'age':34},] 
count = {} 
index = 0 
while index < len(values): 
    if values[index]['id'] in count: 
     del values[index] 
    else: 
     count[values[index]['id']] = 1 
     index += 1 

đầu ra:

[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}] 
+1

Bạn cần kiểm tra điều này thêm một chút. Sửa đổi danh sách trong khi bạn đang lặp qua nó có thể không phải lúc nào cũng hoạt động như bạn mong đợi –

+0

@gnibbler điểm rất tốt! Tôi sẽ xóa câu trả lời và kiểm tra kỹ hơn. –

+0

@gnibbler là tốt hơn? –

6
a = [ 
{'id':1,'name':'john', 'age':34}, 
{'id':1,'name':'john', 'age':34}, 
{'id':2,'name':'hanna', 'age':30}, 
] 

b = {x['id']:x for x in a}.values() 

print(b) 

kết quả đầu ra:

[{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

+0

Trong cùng một ví dụ. làm thế nào tôi có thể nhận được các dicts chỉ chứa các ID tương tự? – user8162

+0

@ user8162, bạn muốn kết quả trông như thế nào? –

+0

Đôi khi, tôi sẽ có cùng một ID, nhưng độ tuổi khác nhau. do đó, đầu ra là [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}]. Tóm lại, nếu các ID giống nhau, thì hãy kết hợp nội dung của những người khác vào một danh sách như tôi đã đề cập ở đây. Cảm ơn trước. – user8162

16

Bạn có thể sử dụng thư viện NumPy (chỉ hoạt động cho Python2.x chỉ):

import numpy as np 

    list_of_unique_dicts=list(np.unique(np.array(list_of_dicts))) 
+6

Nhận lỗi 'LoạiError: các loại không thể đặt hàng: dict()> dict()' khi thực hiện việc này trong Python 3.5. – Guillochon

7

Trong trường hợp các từ điển chỉ được nhận dạng duy nhất bởi tất cả các mục (ID không có sẵn), bạn có thể sử dụng câu trả lời bằng cách sử dụng JSON. Sau đây là một thay thế không sử dụng JSON và sẽ hoạt động miễn là tất cả các giá trị từ điển đều không thay đổi

[dict(s) for s in set(frozenset(d.items()) for d in L)] 
0

Giải pháp nhanh chóng và bẩn chỉ bằng cách tạo danh sách mới.

sortedlist = [] 

for item in listwhichneedssorting: 
    if item not in sortedlist: 
     sortedlist.append(item) 
1

Mở rộng trên John La Rooy (Python - List of unique dictionaries) câu trả lời, làm cho nó linh hoạt hơn một chút:

def dedup_dict_list(list_of_dicts: list, columns: list) -> list: 
    return list({''.join(row[column] for column in columns): row 
       for row in list_of_dicts}.values()) 

Calling Chức năng:

sorted_list_of_dicts = dedup_dict_list(
    unsorted_list_of_dicts, ['id', 'name']) 
Các vấn đề liên quan