web-dev-qa-db-fra.com

Python - Liste de dictionnaires uniques

Disons que j'ai une liste de dictionnaires:

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

et je dois obtenir une liste de dictionnaires uniques (en supprimant les doublons):

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

Quelqu'un peut-il m'aider avec le moyen le plus efficace d'y parvenir en Python?

105
Limaaf

Faites donc un dict temporaire avec la clé id. Ceci filtre les doublons . La values() du dict sera la liste

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

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

En Python 2.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'}]
170
John La Rooy

Le moyen habituel de rechercher uniquement les éléments communs d'un ensemble consiste à utiliser la classe set de Python. Il suffit d’ajouter tous les éléments à l’ensemble, puis de le convertir en list et de supprimer les doublons.

Le problème, bien sûr, est qu'un set() ne peut contenir que des entrées pouvant être remplies et qu'un dict ne l'est pas.

Si j'avais ce problème, ma solution serait de convertir chaque dict en une chaîne qui représente le dict, puis d'ajouter toutes les chaînes à un set(), puis de lire les valeurs de chaîne en tant que list() et de les reconvertir en dict.

Le format JSON est une bonne représentation de dict sous forme de chaîne. Et Python a un module intégré pour JSON (appelé json bien sûr).

Le problème restant est que les éléments d'une dict ne sont pas ordonnés et lorsque Python convertit la dict en une chaîne JSON, vous pouvez obtenir deux chaînes JSON qui représentent des dictionnaires équivalents mais ne sont pas des chaînes identiques. La solution simple consiste à passer l'argument sort_keys=True lorsque vous appelez json.dumps()

EDIT: Cette solution supposait qu’une donnée dict pouvait avoir une partie différente. Si nous pouvons supposer que chaque dict avec la même valeur "id" correspond à chaque dict avec la même valeur "id", il s'agit d'un overkill; La solution de @ gnibbler serait plus rapide et plus simple.

EDIT: Il y a maintenant un commentaire d'André Lima qui dit explicitement que si l'ID est un doublon, il est prudent de supposer que la totalité de la dict est un duplicata. Donc, cette réponse est exagérée et je recommande la réponse de @ gnibbler.

61
steveha

Vous pouvez utiliser la bibliothèque numpy (ne fonctionne que pour Python2.x):

   import numpy as np 

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

Si les dictionnaires ne sont identifiés que de manière unique par tous les éléments (l'ID n'est pas disponible), vous pouvez utiliser la réponse à l'aide de JSON. Voici une alternative qui n'utilise pas JSON et fonctionne tant que toutes les valeurs du dictionnaire sont immuables.

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

Voici une solution assez compacte, même si je soupçonne qu’elle n’est pas particulièrement efficace (pour le dire gentiment):

>>> 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'}]
12
Greg E.

Puisque id est suffisant pour détecter les doublons, et que id est hashable: exécutez-les dans un dictionnaire dont la clé est id. La valeur pour chaque clé est le dictionnaire d'origine.

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

En Python 3, values() ne renvoie pas de liste; vous devrez envelopper tout le côté droit de cette expression dans list(), et vous pouvez écrire la viande de l'expression de manière plus économique en tant que compréhension dictée:

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

Notez que le résultat ne sera probablement pas dans le même ordre que l'original. Si cela est nécessaire, vous pouvez utiliser un Collections.OrderedDict au lieu d'une dict.

Soit dit en passant, il peut être judicieux de simplement conserver les données dans un dictionnaire utilisant la clé id comme clé pour commencer.

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

les sorties:

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

5
Yusuf X

Développement de John La Rooy ( Python - Liste de dictionnaires uniques ), ce qui le rend un peu plus flexible:

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

Fonction d'appel:

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

On peut faire avec 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'}]
_

Remarquez légèrement différent de la réponse accepter.

drop_duplicates vérifiera toutes les colonnes de pandas, si toutes identiques, la ligne sera supprimée.

Par exemple :

Si nous changeons le 2nd dict nom de john en 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'}]
_
1
WeNYoBen

En python 3.6+ (ce que j'ai testé), utilisez simplement:

import json

#Toy example, but will also work for your case 
myListOfDictionaries = [{'a':1,'b':2},{'a':1,'b':2},{'a':1,'b':3}]

myListOfUniqueDictionaries = list(map(json.loads,set(list(map(json.dumps, myListOfDictionaries)))))

print(myListOfUniqueDictionaries)

Explication: nous mappons le json.dumps pour coder les dictionnaires sous forme d'objets json, qui sont immuables. set peut alors être utilisé pour produire un itérable de unique immutables. Enfin, nous reconvertissons notre représentation dans le dictionnaire avec json.loads.

1
VanillaSpinIce

Il y a beaucoup de réponses ici, alors laissez-moi en ajouter une autre:

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
monkut

Je ne sais pas si vous voulez seulement que l'identifiant de vos cartes dans la liste soit unique, mais si l'objectif est d'avoir un ensemble de dict où l'unicité est sur toutes les valeurs des clés .. vous devriez utiliser des clés telles que celle-ci dans votre compréhension:

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

J'espère que cela vous aidera, vous ou une autre personne ayant le problème ...

0
nixmind

Une solution simple et rapide consiste simplement à générer une nouvelle liste.

sortedlist = []

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

Option assez simple:

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
jedwards

Voici une implémentation avec peu de surcharge de mémoire au prix de ne pas être aussi compact que le reste.

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

sortie:

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