Python - Liste der eindeutigen Wörterbücher

158

Angenommen, ich habe eine Liste mit Wörterbüchern:

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

und ich muss eine Liste eindeutiger Wörterbücher erhalten (Entfernen der Duplikate):

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

Kann mir jemand helfen, wie ich dies in Python am effizientesten erreichen kann?

Limaaf
quelle
5
Wie umfangreich sind diese Wörterbücher? Benötigen Sie eine individuelle Attributprüfung, um Duplikate zu ermitteln, oder ist die Überprüfung eines einzelnen Werts in diesen ausreichend?
GDDC
Diese Diktate haben 8 Schlüssel: Wert-Paare und die Liste hat 200 Diktate. Sie haben tatsächlich eine ID und es ist für mich sicher, das Diktat aus der Liste zu entfernen, wenn der gefundene ID-Wert ein Duplikat ist.
Limaaf
Forzenset ist eine effektive Option. set(frozenset(i.items()) for i in list)
Abhijeet

Antworten:

238

Machen Sie also ein vorübergehendes Diktat, wobei der Schlüssel der ist id. Dies filtert die Duplikate heraus. Das values()des Diktats wird die Liste sein

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

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

In 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
quelle
@ John La Rooy - wie könnte man das gleiche verwenden, um Wörterbücher aus einer Liste zu entfernen, die auf mehreren Attributen basiert, dies versucht hat, aber nicht zu funktionieren scheint> {v ['Flug'] ['lon'] ['lat']: v für v in stream} .values ​​()
Jorge Vidinha
1
@JorgeVidinha Angenommen, jeder kann in str (oder Unicode) umgewandelt werden. Versuchen Sie Folgendes: {str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()Dadurch wird nur ein eindeutiger Schlüssel erstellt, der auf Ihren Werten basiert. Like'MH370:-21.474370,86.325589'
Whunterknight
4
@JorgeVidinha, können Sie ein Tupel als Wörterbuchschlüssel verwenden{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy
Beachten Sie, dass dies die Reihenfolge der Wörterbücher in der Liste ändern kann! Verwenden Sie OrderedDictaus collections list(OrderedDict((v['id'], v) for v in L).values()) oder sortieren Sie die resultierende Liste, wenn das für Sie besser funktioniert
Gevra
Wenn Sie alle berücksichtigten Werte und nicht nur die ID benötigen, die Sie verwenden können, verwenden list({str(i):i for i in L}.values())wir str (i), um eine eindeutige Zeichenfolge zu erstellen, die das Wörterbuch darstellt, das zum Filtern der Duplikate verwendet wird.
DelboyJay
79

Der übliche Weg, nur die gemeinsamen Elemente in einer Menge zu finden, ist die Verwendung der Python- setKlasse. Fügen Sie einfach alle Elemente zum Set hinzu, konvertieren Sie das Set in a listund bam, die Duplikate sind verschwunden.

Das Problem ist natürlich, dass a set()nur hashbare Einträge enthalten kann und a dictnicht hashbar ist.

Wenn ich dieses Problem hätte, würde meine Lösung darin bestehen, jede dictin eine Zeichenfolge zu konvertieren , die die Zeichenfolge darstellt dict, dann alle Zeichenfolgen zu a hinzuzufügen und dann die Zeichenfolgenwerte als a set()auszulesen list()und zurück in zu konvertieren dict.

Eine gute Darstellung einer dictZeichenfolge ist das JSON-Format. Und Python hat ein eingebautes Modul für JSON ( jsonnatürlich genannt).

Das verbleibende Problem besteht darin, dass die Elemente in a dictnicht geordnet sind. Wenn Python die dictin eine JSON-Zeichenfolge konvertiert , erhalten Sie möglicherweise zwei JSON-Zeichenfolgen, die äquivalente Wörterbücher darstellen, aber keine identischen Zeichenfolgen sind. Die einfache Lösung besteht darin, das Argument sort_keys=Truebeim Aufruf zu übergeben json.dumps().

EDIT: Diese Lösung ging davon aus, dass ein gegebener dictTeil einen anderen Teil haben könnte. Wenn wir davon ausgehen können, dass jeder dictmit demselben "id"Wert mit jedem dictmit demselben "id"Wert übereinstimmt, ist dies ein Overkill. Die Lösung von @ gnibbler wäre schneller und einfacher.

EDIT: Jetzt gibt es einen Kommentar von André Lima, der ausdrücklich sagt, dass wenn die ID ein Duplikat ist, man davon ausgehen kann, dass das Ganze dictein Duplikat ist. Diese Antwort ist also übertrieben und ich empfehle die Antwort von @ gnibbler.

steveha
quelle
Danke für die Hilfe steveha. Ihre Antwort gab mir tatsächlich einige Kenntnisse, die ich nicht hatte, da ich gerade mit Python angefangen habe =)
Limaaf
1
Obwohl der Ausweis in diesem speziellen Fall übertrieben ist, ist dies immer noch eine ausgezeichnete Antwort!
Josh Werts
8
Dies hilft mir, da mein Wörterbuch keinen Schlüssel hat und nur durch alle seine Einträge eindeutig identifiziert wird. Vielen Dank!
Erico
Diese Lösung funktioniert die meiste Zeit, aber es kann zu Leistungsproblemen beim Skalieren kommen, aber der Autor weiß dies meiner Meinung nach und empfiehlt daher die Lösung mit "id". Leistungsprobleme: Diese Lösung verwendet die Serialisierung zum Stringing und dann zum Deserialisieren ... Das Serialisieren / Deserialisieren ist eine teure Berechnung und lässt sich normalerweise nicht gut skalieren (Anzahl der Elemente ist n> 1e6 oder jedes Wörterbuch enthält> 1e6 Elemente oder beides) oder wenn Sie haben um dies viele Male> 1e6 oder oft auszuführen.
Trevor Boyd Smith
Kurz gesagt, diese Lösung zeigt ein großartiges kanonisches Beispiel dafür, warum Sie Ihre Lösung entwerfen möchten ... dh wenn Sie eine eindeutige ID haben ... dann können Sie effizient auf die Daten zugreifen ... wenn Sie faul sind Wenn Sie keine ID haben, ist Ihr Datenzugriff teurer.
Trevor Boyd Smith
21

Falls die Wörterbücher nur durch alle Elemente eindeutig identifiziert werden (ID ist nicht verfügbar), können Sie die Antwort mit JSON verwenden. Das Folgende ist eine Alternative, die JSON nicht verwendet und funktioniert, solange alle Wörterbuchwerte unveränderlich sind

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

Sie können die Numpy-Bibliothek verwenden (funktioniert nur für Python2.x):

   import numpy as np 

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

Damit es mit Python 3.x (und neueren Versionen von numpy) funktioniert, müssen Sie ein Array von Dicts in ein numpy-Array von Zeichenfolgen konvertieren, z

list_of_unique_dicts=list(np.unique(np.array(list_of_dicts).astype(str)))
Blase
quelle
13
Erhalten Sie den Fehler, TypeError: unorderable types: dict() > dict()wenn Sie dies in Python 3.5 tun.
Guillochon
16

Hier ist eine einigermaßen kompakte Lösung, obwohl ich vermute, dass sie nicht besonders effizient ist (um es milde auszudrücken):

>>> 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.
quelle
3
Umgeben Sie den map()Aufruf list()in Python 3, um eine Liste zurückzugewinnen, andernfalls handelt es sich um ein mapObjekt.
dmn
Ein zusätzlicher Vorteil dieses Ansatzes in Python 3.6+ ist, dass die Listenreihenfolge beibehalten wird
jnnnnn
7

Da das idzum Erkennen von Duplikaten ausreicht und das idHash-fähig ist: Führen Sie sie durch ein Wörterbuch, das idden Schlüssel als Schlüssel hat. Der Wert für jeden Schlüssel ist das Originalwörterbuch.

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

Gibt in Python 3 values()keine Liste zurück. Sie müssen die gesamte rechte Seite dieses Ausdrucks einwickeln list(), und Sie können das Fleisch des Ausdrucks als Diktatverständnis wirtschaftlicher schreiben:

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

Beachten Sie, dass das Ergebnis wahrscheinlich nicht in derselben Reihenfolge wie das Original angezeigt wird. Wenn dies erforderlich ist, können Sie a Collections.OrderedDictanstelle von a verwenden dict.

Abgesehen davon kann es sehr sinnvoll sein , die Daten nur in einem Wörterbuch zu speichern, das das verwendetid Schlüssel as .

irgendwie
quelle
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)

Ausgänge:

[{'Alter': 34, 'ID': 1, 'Name': 'John'}, {'Alter': 30, 'ID': 2, 'Name': 'Hanna'}]

Yusuf X.
quelle
Im selben Beispiel. Wie kann ich die Diktate erhalten, die nur ähnliche IDs enthalten?
user8162
@ user8162, wie soll die Ausgabe aussehen?
Yusuf X
Manchmal habe ich den gleichen Ausweis, aber ein anderes Alter. Die Ausgabe lautet also [{'age': [34, 40], 'id': 1, 'name': ['john', Peter]}]. Kurz gesagt, wenn die IDs identisch sind, kombinieren Sie den Inhalt anderer zu einer Liste, wie ich hier erwähnt habe. Danke im Voraus.
user8162
1
b = {x ['id']: [y für y in a wenn y ['id'] == x ['id']] für x in a} ist eine Möglichkeit, sie zu gruppieren.
Yusuf X
4

Erweitern Sie die Antwort von John La Rooy ( Python - Liste der eindeutigen Wörterbücher ), um sie etwas flexibler zu gestalten:

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

Aufruffunktion:

sorted_list_of_dicts = dedup_dict_list(
    unsorted_list_of_dicts, ['id', 'name'])
Illegaler Betreiber
quelle
4

Wir können damit machen 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'}]

Beachten Sie etwas anders als die Antwort akzeptieren.

drop_duplicates überprüft alle Spalten in Pandas, wenn alle gleich sind, wird die Zeile gelöscht.

Zum Beispiel :

Wenn wir den 2. dictNamen von John in Peter ändern

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

Verwenden Sie in Python 3.6+ (was ich getestet habe) einfach:

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)

Erläuterung: Wir json.dumpsordnen die Wörterbücher als json-Objekte zu, die unveränderlich sind. setkann dann verwendet werden, um eine Iterable von einzigartigen unveränderlichen zu erzeugen . Schließlich konvertieren wir mit in unsere Wörterbuchdarstellung zurück json.loads. Beachten Sie, dass zunächst nach Schlüsseln sortiert werden muss, um die Wörterbücher in einer eindeutigen Form anzuordnen. Dies gilt für Python 3.6+, da Wörterbücher standardmäßig sortiert sind.

VanillaSpinIce
quelle
1
Denken Sie daran, die Schlüssel zu sortieren, bevor Sie sie an JSON senden. Sie müssen auch nicht konvertieren, listbevor Sie dies tun set.
Nathan
2

Ich habe meine Favoriten zum Ausprobieren zusammengefasst:

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

Eine schnelle und schmutzige Lösung besteht darin, einfach eine neue Liste zu erstellen.

sortedlist = []

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

Ich weiß nicht, ob Sie nur möchten, dass die ID Ihrer Diktate in der Liste eindeutig ist, aber wenn das Ziel darin besteht, ein Diktat zu erstellen, bei dem die Einheitlichkeit auf allen Schlüsselwerten liegt. Sie sollten einen solchen Tupelschlüssel verwenden in Ihrem Verständnis:

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

Hoffe, es hilft Ihnen oder einer anderen Person, die das Problem hat ...

Nixmind
quelle
1

Hier gibt es viele Antworten, also lassen Sie mich noch eine hinzufügen:

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)
Mönch
quelle
0

Ziemlich einfache Option:

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

Nun, alle hier erwähnten Antworten sind gut, aber bei einigen Antworten kann es zu Fehlern kommen, wenn die Wörterbuchelemente eine verschachtelte Liste oder ein verschachteltes Wörterbuch haben. Daher schlage ich eine einfache Antwort vor

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

Hier ist eine Implementierung mit geringem Speicheraufwand, die nicht so kompakt ist wie die anderen.

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

Ausgabe:

[{'age': 30, 'id': 2, 'name': 'hanna'}, {'age': 34, 'id': 1, 'name': 'john'}]
Samy Vilar
quelle
1
Sie müssen dies etwas mehr testen. Das Ändern der Liste, während Sie darüber iterieren, funktioniert möglicherweise nicht immer wie erwartet
John La Rooy
@gnibbler sehr guter Punkt! Ich werde die Antwort löschen und gründlicher testen.
Samy Vilar
Sieht besser aus. Sie können ein Set verwenden, um die IDs anstelle des Diktats zu verfolgen. Überlegen Sie, ob Sie mit indexat beginnen len(values)und rückwärts zählen möchten. Dies bedeutet, dass Sie immer dekrementieren können, indexob Sie deloder nicht. zBfor index in reversed(range(len(values))):
John La Rooy
@gnibbler interessant, sehen Sets fast ständig wie Wörterbücher aus?
Samy Vilar
-4

Dies ist die Lösung, die ich gefunden habe:

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

Grundsätzlich prüfen Sie, ob die ID in der Liste vorhanden ist. Wenn dies der Fall ist, löschen Sie das Wörterbuch. Wenn nicht, hängen Sie die ID an die Liste an

Tabchas
quelle
Ich würde eher ein Set als eine Liste für usedID verwenden. Es ist eine schnellere Suche und besser lesbar
Happydave
Ja, ich wusste nichts über Sets ... aber ich lerne ... Ich habe mir gerade die Antwort von @gnibbler angesehen ...
Tabchas
1
Sie müssen dies etwas mehr testen. Das Ändern der Liste, während Sie darüber iterieren, funktioniert möglicherweise nicht immer wie erwartet
John La Rooy
Ja, ich verstehe nicht, warum es nicht funktioniert ... Irgendwelche Ideen, was ich falsch mache?
Tabchas
Nein, ich habe das Problem erkannt ... es ist nur so, dass ich nicht verstehe, warum es dieses Problem verursacht ... weißt du?
Tabchas