Python - Danh sách từ điển độc đáo


158

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

[
    {'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},
]

Ai đó có thể giúp tôi cách hiệu quả nhất để đạt được điều này trong Python không?


5
Làm thế nào rộng rãi là những từ điển? Bạn có cần kiểm tra thuộc tính riêng lẻ để xác định trùng lặp hoặc kiểm tra một giá trị duy nhất trong chúng không?
gddc

Các ký tự này có 8 khóa: cặp giá trị và danh sách có 200 ký tự. Họ thực sự đã có một ID và nó an toàn cho tôi để xóa dict khỏi danh sách nếu giá trị ID được tìm thấy là một bản sao.
Limaaf


hàng chục là một lựa chọn hiệu quả. set(frozenset(i.items()) for i in list)
Abhijeet

Câu trả lời:


238

Vì vậy, thực hiện một lệnh tạm thời với chìa khóa là id. Điều này lọc ra các bản sao. Các values()của dict sẽ 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'}]

@ John La Rooy - làm thế nào người ta 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 dường như không hoạt động> {v ['chuyến bay'] ['lon'] ['lat']: v cho v trong luồng} .values ​​()
Jorge Vidinha

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

4
@JorgeVidinha, bạn có thể sử dụng bộ dữ liệu làm khóa từ điển{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy

lưu ý rằng điều này có thể thay đổi thứ tự của các từ điển trong danh sách! sử dụng OrderedDicttừ collections list(OrderedDict((v['id'], v) for v in L).values()) hoặc sắp xếp danh sách kết quả nếu điều đó tốt hơn cho bạn
gevra

Nếu bạn cần tất cả các giá trị được xem xét và không chỉ ID bạn có thể sử dụng list({str(i):i for i in L}.values())Ở đây chúng tôi sử dụng str (i) để tạo một chuỗi duy nhất đại diện cho từ điển được sử dụng để lọc các bản sao.
DelboyJay

79

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

Tất nhiên, vấn đề là một set()chỉ có thể chứa các mục có thể băm và một dictkhông thể băm.

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

Một đại diện tốt của một dictdạng chuỗi là định dạng JSON. Và Python có một mô-đun tích hợp cho JSON ( jsontất nhiên được gọi là ).

Vấn đề còn lại là các phần tử trong một dictkhông được sắp xếp theo thứ tự và khi Python chuyển đổi thành dictchuỗ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à các chuỗi giống hệt nhau. Giải pháp dễ dàng là vượt qua đối số sort_keys=Truekhi bạn gọi json.dumps().

EDIT: Giải pháp này đã giả định rằng một cái nhất định dictcó 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 thứ dictcó cùng "id"giá trị sẽ khớp với nhau dictvới cùng một "id"giá trị, thì điều này là quá mức cần thiết; Giải pháp @ gnibbler's sẽ nhanh hơn và dễ dàng hơn.

EDIT: Bây giờ có một nhận xét từ André Lima nói rõ ràng rằng nếu ID là một bản sao, sẽ an toàn khi cho rằng toàn bộ dictlà 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 trả lời @ gnibbler's.


Cảm ơn sự giúp đỡ 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ó, vì tôi mới bắt đầu với Python =)
Limaaf

1
Mặc dù 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!
Josh Werts

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

Giải pháp này hoạt động hầu hết thời gian nhưng có thể có vấn đề về hiệu suất khi nhân rộng nhưng tác giả tôi nghĩ biết điều này và do đó đề xuất giải pháp với "id". Mối quan tâm về hiệu suất: Giải pháp này sử dụng tuần tự hóa thành chuỗi và sau đó giải tuần tự hóa ... tuần tự hóa / giải tuần tự hóa là tính toán đắt tiền và thường không tăng quy mô tốt (số mục là n> 1e6 hoặc mỗi từ điển chứa> 1e6 mục hoặc cả hai) hoặc nếu bạn có để thực hiện điều này nhiều lần> 1e6 hoặc thường xuyên.
Trevor Boyd Smith

Chỉ cần một thời gian ngắn giải pháp này minh họa một ví dụ kinh điển tuyệt vời về lý do tại sao bạn muốn thiết kế giải pháp của mình ... tức là nếu bạn có một id duy nhất ... thì bạn có thể truy cập dữ liệu một cách hiệu quả ... nếu bạn lười biếng và không có id thì truy cập dữ liệu của bạn đắt hơn.
Trevor Boyd Smith

21

Trong trường hợp từ điển chỉ được xác định 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 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 là bất biến

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

18

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

   import numpy as np 

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

Để làm cho nó hoạt động với Python 3.x (và các phiên bản gần đây của numpy), bạn cần chuyển đổi mảng dicts thành mảng chuỗi numpy, ví dụ:

list_of_unique_dicts=list(np.unique(np.array(list_of_dicts).astype(str)))

13
Nhận lỗi TypeError: unorderable types: dict() > dict()khi làm điều này trong Python 3.5.
Guillhol

16

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

>>> 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'}]

3
Bao quanh map()cuộc gọi với list()Python 3 để lấy lại danh sách, nếu không thì đó là một mapđối tượng.
dmn

một lợi ích bổ sung của phương pháp này trong python 3.6+ là việc sắp xếp danh sách được giữ nguyên
jnnnnn

7

Vì điều idnày là đủ để phát hiện các bản sao và idcó thể băm: chạy chúng thông qua một từ điển có idkhó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 bao bọc toàn bộ phía bên phải của biểu thức đó list()và bạn có thể viết phần thịt của biểu thức một cách kinh tế hơn như một cách hiểu chính tả:

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 theo thứ tự như ban đầu. Nếu đó là một yêu cầu, bạn có thể sử dụng Collections.OrderedDictthay vì mộtdict .

Bên cạnh đó, có thể có ý nghĩa tốt khi chỉ giữ dữ liệu trong một từ điển sử dụng idkhóa làm khóa để bắt đầu.


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)

đầu ra:

[{'Tuổi': 34, 'id': 1, 'tên': 'john'}, {'tuổi': 30, 'id': 2, 'tên': 'hanna'}]


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ự?
dùng8162

@ user8162, bạn muốn đầu ra trông như thế nào?
Yusuf X

Đôi khi, tôi sẽ có cùng một ID, nhưng độ tuổi khác nhau. vì vậy, đầu ra là [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}]. Nói tóm lại, nếu 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.
dùng8162

1
b = {x ['id']: [y for y in a if y ['id'] == x ['id']] cho x in a} là một cách để nhóm chúng lại với nhau.
Yusuf X

4

Mở rộng trên câu trả lời của John La Rooy ( Python - Danh sách từ điển duy nhất ), 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())

Chức năng gọi:

sorted_list_of_dicts = dedup_dict_list(
    unsorted_list_of_dicts, ['id', 'name'])

4

Chúng ta có thể làm với pandas

import pandas as pd
yourdict=pd.DataFrame(L).drop_duplicates().to_dict('r')
Out[293]: [{'age': 34, 'id': 1, 'name': 'john'}, {'age': 30, 'id': 2, 'name': 'hanna'}]

Thông báo hơi khác với câu trả lời chấp nhận.

drop_duplicates sẽ kiểm tra tất cả các cột trong gấu trúc, nếu tất cả giống nhau thì hàng sẽ bị loại bỏ.

Ví dụ :

Nếu chúng ta thay đổi dicttên thứ 2 từ john thành peter

L=[
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'peter', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]
pd.DataFrame(L).drop_duplicates().to_dict('r')
Out[295]: 
[{'age': 34, 'id': 1, 'name': 'john'},
 {'age': 34, 'id': 1, 'name': 'peter'},# here will still keeping the dict in the out put 
 {'age': 30, 'id': 2, 'name': 'hanna'}]

2

Trong python 3.6+ (những gì tôi đã thử nghiệm), chỉ cần sử dụng:

import json

#Toy example, but will also work for your case 
myListOfDicts = [{'a':1,'b':2},{'a':1,'b':2},{'a':1,'b':3}]
#Start by sorting each dictionary by keys
myListOfDictsSorted = [sorted(d.items()) for d in myListOfDicts]

#Using json methods with set() to get unique dict
myListOfUniqueDicts = list(map(json.loads,set(map(json.dumps, myListOfDictsSorted))))

print(myListOfUniqueDicts)

Giải thích: chúng tôi đang lập bản đồ json.dumpsđể mã hóa từ điển dưới dạng đối tượng json, là bất biến. setsau đó có thể được sử dụng để tạo ra một lần lặp các bất biến duy nhất . Cuối cùng, chúng tôi chuyển đổi trở lại đại diện từ điển của chúng tôi bằng cách sử dụng json.loads. Lưu ý rằng ban đầu, người ta phải sắp xếp theo các phím để sắp xếp từ điển theo một hình thức duy nhất. Điều này hợp lệ với Python 3.6+ vì từ điển được sắp xếp theo mặc định.


1
Hãy nhớ sắp xếp các khóa trước khi chuyển sang JSON. Bạn cũng không cần phải chuyển đổi listtrước khi làm set.
Nathan

2

Tôi đã tóm tắt các mục yêu thích của tôi để thử:

https://repl.it/@SmaMa/Python-List-of-unique-dictionaries

# ----------------------------------------------
# Setup
# ----------------------------------------------

myList = [
  {"id":"1", "lala": "value_1"},
  {"id": "2", "lala": "value_2"}, 
  {"id": "2", "lala": "value_2"}, 
  {"id": "3", "lala": "value_3"}
]
print("myList:", myList)

# -----------------------------------------------
# Option 1 if objects has an unique identifier
# -----------------------------------------------

myUniqueList = list({myObject['id']:myObject for myObject in myList}.values())
print("myUniqueList:", myUniqueList)

# -----------------------------------------------
# Option 2 if uniquely identified by whole object
# -----------------------------------------------

myUniqueSet = [dict(s) for s in set(frozenset(myObject.items()) for myObject in myList)]
print("myUniqueSet:", myUniqueSet)

# -----------------------------------------------
# Option 3 for hashable objects (not dicts)
# -----------------------------------------------

myHashableObjects = list(set(["1", "2", "2", "3"]))
print("myHashAbleList:", myHashableObjects)

1

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

sortedlist = []

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

1

Tôi không biết nếu bạn chỉ muốn id của các ký tự của bạn trong danh sách là duy nhất, nhưng nếu mục tiêu là có một bộ lệnh trong đó tính đơn nhất trên tất cả các giá trị của khóa .. bạn nên sử dụng khóa tuples như thế này trong sự hiểu biết của bạn:

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

Hy vọng nó sẽ giúp bạn hoặc người khác có mối quan tâm ....


1

Có rất nhiều câu trả lời ở đây, vì vậy hãy để tôi thêm một câu trả lời khác:

import json
from typing import List

def dedup_dicts(items: List[dict]):
    dedupped = [ json.loads(i) for i in set(json.dumps(item, sort_keys=True) for item in items)]
    return dedupped

items = [
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 1, 'name': 'john', 'age': 34},
    {'id': 2, 'name': 'hanna', 'age': 30},
]
dedup_dicts(items)

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

Vâng tất cả các câu trả lời được đề cập ở đây đều tốt, nhưng trong một số câu trả lời người ta có thể gặp lỗi nếu các mục từ điển có danh sách hoặc từ điển lồng nhau, vì vậy tôi đề xuất câu trả lời đơn giản

a = [str(i) for i in a]
a = list(set(a))
a = [eval(i) for i in a]

-1

Đây là một triển khai với ít bộ nhớ trên đầu vớ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 nhiều hơn một chút. Sửa đổi danh sách trong khi bạn lặp đi lặp lại nó có thể không luôn hoạt động như bạn mong đợi
John La Rooy

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

Trông tốt hơn. Bạn có thể sử dụng một bộ để theo dõi các id thay vì dict. Xem xét bắt đầu indextại len(values)và đếm ngược, điều đó có nghĩa là bạn luôn có thể giảm indexcho dù bạn có delhay không. ví dụ:for index in reversed(range(len(values))):
John La Rooy

@gnibbler thú vị, các bộ có nhìn gần như liên tục như từ điển không?
Samy Vilar

-4

Đâ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ó trong danh sách hay không, nếu có, hãy xóa từ điển, nếu không, hãy thêm ID vào danh sách


Tôi sẽ sử dụng một tập hợp chứ không phải danh sách cho usedID. Đó là một tra cứu nhanh hơn và dễ đọc hơn
happydave

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

1
Bạn cần 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 đi lặp lại nó có thể không luôn hoạt động như bạn mong đợi
John La Rooy

Phải tôi không hiểu tại sao nó không hoạt động ... Có ai biết tôi đang làm gì sai không?
tabchas

Không, tôi đã bắt gặp vấn đề ... chỉ là tôi không hiểu tại sao nó lại đưa ra vấn đề đó ... bạn có biết không?
tabchas
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.