Python - Daftar kamus unik

158

Katakanlah saya punya daftar kamus:

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

dan saya perlu mendapatkan daftar kamus unik (menghapus duplikat):

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

Adakah yang bisa membantu saya dengan cara paling efisien untuk mencapai ini dengan Python?

Limaaf
sumber
5
Seberapa luas kamus-kamus ini? Apakah Anda memerlukan pengecekan atribut individual untuk menentukan duplikat, atau memeriksa nilai tunggal di dalamnya cukup?
gddc
Dicts ini memiliki 8 kunci: pasangan nilai dan daftar mendapat 200 dicts. Mereka benar-benar mendapat ID dan aman bagi saya untuk menghapus dict dari daftar jika nilai ID yang ditemukan adalah duplikat.
Limaaf
Kemungkinan duplikat dari Cara membuat nilai dalam daftar kamus unik?
Abhijeet
forzenset adalah opsi yang efektif. set(frozenset(i.items()) for i in list)
Abhijeet

Jawaban:

238

Jadi buat dikt sementara dengan kuncinya adalah id. Ini memfilter duplikat. The values()dict akan daftar

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

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

Dalam 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
sumber
@John La Rooy - bagaimana seseorang dapat menggunakan yang sama untuk menghapus kamus dari daftar berdasarkan beberapa atribut, mencoba ini tetapi tampaknya tidak berfungsi> {v ['penerbangan'] ['lon'] ['lat']: v for v in stream} .values ​​()
Jorge Vidinha
1
@JorgeVidinha dengan asumsi masing-masing dapat dilemparkan ke str (atau unicode), coba ini: {str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()Ini hanya membuat kunci unik berdasarkan nilai-nilai Anda. Seperti'MH370:-21.474370,86.325589'
whunterknight
4
@JorgeVidinha, Anda dapat menggunakan tuple sebagai kunci kamus{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy
perhatikan bahwa ini dapat mengubah urutan kamus dalam daftar! gunakan OrderedDictdari collections list(OrderedDict((v['id'], v) for v in L).values()) atau urutkan daftar yang dihasilkan jika itu bekerja lebih baik untuk Anda
gevra
Jika Anda perlu mempertimbangkan semua nilai dan bukan hanya ID, Anda dapat menggunakan list({str(i):i for i in L}.values())Di sini kami menggunakan str (i) untuk membuat string unik yang mewakili kamus yang digunakan untuk memfilter duplikat.
DelboyJay
79

Cara biasa untuk menemukan hanya elemen-elemen umum dalam set adalah dengan menggunakan setkelas Python . Cukup tambahkan semua elemen ke set, lalu konversikan set ke list, dan bam duplikatnya hilang.

Masalahnya, tentu saja, adalah bahwa set()hanya dapat berisi entri hashable, dan a dicttidak hashable.

Jika saya memiliki masalah ini, solusi saya adalah untuk mengkonversi masing dict- masing menjadi string yang mewakili dict, kemudian menambahkan semua string ke set()kemudian membaca nilai-nilai string sebagai list()dan dikonversi kembali ke dict.

Representasi yang baik dari dictdalam bentuk string adalah format JSON. Dan Python memiliki modul bawaan untuk JSON (disebut jsontentu saja).

Masalah yang tersisa adalah bahwa elemen-elemen dalam a dicttidak diurutkan, dan ketika Python mengonversinya dictmenjadi string JSON, Anda mungkin mendapatkan dua string JSON yang mewakili kamus yang setara tetapi bukan string yang identik. Solusi mudahnya adalah meneruskan argumen sort_keys=Truesaat Anda menelepon json.dumps().

EDIT: Solusi ini mengasumsikan bahwa yang diberikan dictdapat memiliki bagian yang berbeda. Jika kita dapat berasumsi bahwa setiap dictdengan nilai yang sama "id"akan cocok dictsatu sama lain dengan nilai yang sama "id", maka ini berlebihan; Solusi @ gnibbler akan lebih cepat dan mudah.

EDIT: Sekarang ada komentar dari André Lima secara eksplisit mengatakan bahwa jika ID adalah duplikat, aman untuk menganggap bahwa keseluruhan dictadalah duplikat. Jadi jawaban ini berlebihan dan saya merekomendasikan jawaban @ gnibbler.

steveha
sumber
Terima kasih atas bantuan steveha. Jawaban Anda benar-benar memberi saya pengetahuan yang tidak saya miliki, karena saya baru saja mulai dengan Python =)
Limaaf
1
Meskipun diberikan terlalu banyak ID dalam kasus khusus ini, ini masih merupakan jawaban yang sangat baik!
Josh Werts
8
Ini membantu saya karena kamus saya tidak memiliki kunci, dan hanya diidentifikasi secara unik oleh semua entri. Terima kasih!
ericso
Solusi ini berfungsi sebagian besar waktu tetapi mungkin ada masalah kinerja dengan peningkatan tetapi penulis saya pikir tahu ini dan karena itu merekomendasikan solusi dengan "id". Masalah kinerja: Solusi ini menggunakan serialisasi ke string dan kemudian deserializing ... serialisasi / deserializing adalah perhitungan yang mahal dan biasanya tidak meningkatkan skala dengan baik (jumlah item n> 1e6 atau setiap kamus berisi> 1e6 item atau keduanya) atau jika Anda memiliki untuk mengeksekusi ini berkali-kali> 1e6 atau sering.
Trevor Boyd Smith
Singkat kata solusi ini menggambarkan contoh kanonik yang bagus mengapa Anda ingin merancang solusi Anda ... yaitu jika Anda memiliki id yang unik ... maka Anda dapat secara efisien mengakses data ... jika Anda malas dan tidak memiliki id maka akses data Anda lebih mahal.
Trevor Boyd Smith
21

Jika kamus hanya diidentifikasi secara unik oleh semua item (ID tidak tersedia) Anda dapat menggunakan jawabannya menggunakan JSON. Berikut ini adalah alternatif yang tidak menggunakan JSON, dan akan berfungsi selama semua nilai kamus tidak dapat diubah

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

Anda dapat menggunakan library numpy (hanya berfungsi untuk Python2.x):

   import numpy as np 

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

Untuk membuatnya bekerja dengan Python 3.x (dan versi numpy terbaru), Anda perlu mengubah array dicts menjadi array string numpy, misalnya

list_of_unique_dicts=list(np.unique(np.array(list_of_dicts).astype(str)))
gelembung
sumber
13
Dapatkan kesalahan TypeError: unorderable types: dict() > dict()saat melakukan ini di Python 3.5.
Guillochon
16

Berikut ini adalah solusi yang cukup kompak, meskipun saya kira tidak terlalu efisien (secara sederhana):

>>> 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'}]
Greg E.
sumber
3
Kelilingi map()panggilan dengan list()Python 3 untuk mendapatkan daftar kembali, jika tidak itu adalah mapobjek.
dmn
manfaat tambahan dari pendekatan ini dalam python 3.6+ adalah bahwa urutan daftar dipertahankan
jnnnnn
7

Karena idcukup untuk mendeteksi duplikat, dan idhashable: jalankan melalui kamus yang memiliki idsebagai kunci. Nilai untuk setiap tombol adalah kamus asli.

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

Dalam Python 3, values()tidak mengembalikan daftar; Anda harus membungkus seluruh sisi kanan ekspresi itu list(), dan Anda dapat menulis daging ekspresi lebih ekonomis sebagai pemahaman dict:

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

Perhatikan bahwa hasilnya kemungkinan tidak akan berada dalam urutan yang sama seperti aslinya. Jika itu persyaratan, Anda bisa menggunakan Collections.OrderedDicta dict.

Sebagai tambahan, mungkin cukup masuk akal untuk menyimpan data dalam kamus yang menggunakan idkunci as untuk memulai.

baik hati
sumber
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)

output:

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

Yusuf X
sumber
Dalam contoh yang sama. bagaimana saya bisa mendapatkan dicts yang hanya berisi ID yang sama?
user8162
@ user8162, seperti apa output yang Anda inginkan?
Yusuf X
Terkadang, saya akan memiliki ID yang sama, tetapi usianya berbeda. jadi output menjadi [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}]. Singkatnya, jika ID-nya sama, maka gabungkan konten orang lain ke daftar seperti yang saya sebutkan di sini. Terima kasih sebelumnya.
user8162
1
b = {x ['id']: [y untuk y di a jika y ['id'] == x ['id']] untuk x dalam a} adalah salah satu cara untuk mengelompokkannya.
Yusuf X
4

Memperluas jawaban John La Rooy ( Python - Daftar kamus unik ), membuatnya sedikit lebih fleksibel:

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())

Fungsi Panggilan:

sorted_list_of_dicts = dedup_dict_list(
    unsorted_list_of_dicts, ['id', 'name'])
Operator Ilegal
sumber
4

Kita bisa melakukannya 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'}]

Perhatikan sedikit berbeda dari jawaban terima.

drop_duplicates akan memeriksa semua kolom dalam panda, jika semuanya sama maka baris akan dijatuhkan.

Sebagai contoh :

Jika kita mengubah dictnama ke-2 dari john ke 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'}]
YOBEN_S
sumber
2

Dalam python 3.6+ (apa yang telah saya uji), cukup gunakan:

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)

Penjelasan: kami memetakan json.dumpsuntuk menyandikan kamus sebagai objek json, yang tidak dapat diubah. setkemudian dapat digunakan untuk menghasilkan iterable dari kekekalan yang unik . Akhirnya, kami mengonversi kembali ke representasi kamus menggunakan json.loads. Perhatikan bahwa pada awalnya, seseorang harus mengurutkan berdasarkan kunci untuk mengatur kamus dalam bentuk yang unik. Ini berlaku untuk Python 3.6+ karena kamus dipesan secara default.

VanillaSpinIce
sumber
1
Ingatlah untuk menyortir kunci sebelum membuang ke JSON. Anda juga tidak perlu melakukan konversi listsebelum melakukannya set.
Nathan
2

Saya telah merangkum favorit saya untuk dicoba:

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)
Sma Ma
sumber
1

Solusi cepat dan kotor hanya dengan membuat daftar baru.

sortedlist = []

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

Saya tidak tahu apakah Anda hanya ingin id dicts Anda dalam daftar menjadi unik, tetapi jika tujuannya adalah untuk memiliki satu set dict di mana unicity ada pada semua nilai kunci .. Anda harus menggunakan kunci tuple seperti ini dalam pemahaman Anda:

>>> 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

Semoga ini bisa membantu Anda atau orang lain yang memiliki kekhawatiran ....

nixmind
sumber
1

Ada banyak jawaban di sini, jadi izinkan saya menambahkan yang lain:

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)
monkut
sumber
0

Opsi yang cukup mudah:

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
jedwards
sumber
0

Baiklah semua jawaban yang disebutkan di sini bagus, tetapi dalam beberapa jawaban orang dapat menghadapi kesalahan jika item kamus memiliki daftar atau kamus bersarang, jadi saya mengusulkan jawaban sederhana

a = [str(i) for i in a]
a = list(set(a))
a = [eval(i) for i in a]
PRAKHAR KAUSHIK
sumber
-1

Inilah implementasi dengan overhead memori yang kecil dengan biaya tidak sekompak yang lainnya.

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

keluaran:

[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]
Samy Vilar
sumber
1
Anda perlu menguji ini sedikit lagi. Mengubah daftar saat Anda mengulanginya mungkin tidak selalu berfungsi seperti yang Anda harapkan
John La Rooy
@gnibbler poin yang sangat bagus! Saya akan menghapus jawabannya dan mengujinya lebih menyeluruh.
Samy Vilar
Terlihat lebih baik. Anda dapat menggunakan satu set untuk melacak id bukan dict. Pertimbangkan untuk memulai indexpada len(values)dan menghitung mundur, itu berarti bahwa Anda dapat selalu mengurangi indexapakah Anda delatau tidak. mis.for index in reversed(range(len(values))):
John La Rooy
@gnibbler menarik, apakah set memiliki tampilan konstan seperti kamus?
Samy Vilar
-4

Ini adalah solusi yang saya temukan:

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

Pada dasarnya Anda memeriksa apakah ID ada dalam daftar, jika ada, hapus kamus, jika tidak, tambahkan ID ke daftar

tabcha
sumber
Saya akan menggunakan set daripada daftar untuk usedID. Ini pencarian yang lebih cepat, dan lebih mudah dibaca
happydave
Ya saya tidak tahu tentang set ... tapi saya belajar ... Saya hanya melihat jawaban @gnibbler ...
tabchas
1
Anda perlu menguji ini sedikit lagi. Mengubah daftar saat Anda mengulanginya mungkin tidak selalu berfungsi seperti yang Anda harapkan
John La Rooy
Ya saya tidak mengerti mengapa itu tidak berhasil ... Ada gagasan apa yang saya lakukan salah?
tabchas
Tidak, saya menangkap masalahnya ... hanya saja saya tidak mengerti mengapa ini memberikan masalah itu ... tahukah Anda?
tabcha