Python – Список уникальных словарей

Предположим, у меня есть список словарей:

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

и мне нужно получить список уникальных словарей (удаление дубликатов):

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

Может ли кто-нибудь помочь мне с наиболее эффективным способом достижения этого в Python?

Поэтому создайте временный dict с ключом, являющимся id . Это отфильтровывает дубликаты. values() dict будут списком

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

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

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

Обычный способ найти только общие элементы в наборе – это использовать класс set Python. Просто добавьте все элементы в набор, затем преобразуйте набор в list , и bam дубликаты исчезли.

Проблема, конечно, в том, что set() может содержать только хешируемые записи, а dict не является хешируемым.

Если бы у меня была эта проблема, моим решением было бы преобразовать каждый dict в строку, которая представляет dict , а затем добавить все строки в set() затем зачитать строковые значения в виде list() и преобразовать обратно в dict .

Хорошим представлением dict в строковой форме является формат JSON. А у Python есть встроенный модуль для JSON (конечно, он называется json ).

Оставшаяся проблема заключается в том, что элементы в dict не упорядочены, и когда Python преобразует dict в строку JSON, вы можете получить две строки JSON, которые представляют эквивалентные словари, но не идентичные строки. Простое решение – передать аргумент sort_keys=True когда вы вызываете json.dumps() .

EDIT: Это решение предполагало, что данный dict может иметь какую-либо часть. Если мы можем предположить, что каждый dict с тем же значением "id" будет соответствовать любому другому dict с тем же значением "id" , то это будет излишним; Решение @ gnibbler будет быстрее и проще.

EDIT: Теперь есть комментарий Андре Лимы, в котором явным образом говорится, что если идентификатор является дубликатом, можно с уверенностью предположить, что весь dict является дубликатом. Таким образом, этот ответ является излишним, и я рекомендую ответить @ gnibbler.

Вы можете использовать библиотеку numpy (работает только для Python2.x):

  import numpy as np list_of_unique_dicts=list(np.unique(np.array(list_of_dicts))) 

Вот достаточно компактное решение, хотя я подозреваю, что он не особенно эффективен (мягко говоря):

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

Поскольку id достаточен для обнаружения дубликатов, а id – хешируемый: запустите его через словарь с id в качестве ключа. Значение для каждого ключа – это оригинальный словарь.

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

В Python 3 values() не возвращают список; вам нужно обернуть всю правую часть этого выражения в list() , и вы можете написать мясо выражения более экономично, как понимание dict:

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

Обратите внимание, что результат, вероятно, не будет в том же порядке, что и оригинал. Если это требование, вы можете использовать Collections.OrderedDict вместо dict .

В стороне, это может иметь большой смысл просто хранить данные в словаре, который использует id как ключ для начала.

 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) 

выходы:

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

Если словари уникальны для всех элементов (идентификатор недоступен), вы можете использовать ответ, используя JSON. Ниже приведена альтернатива, которая не использует JSON и будет работать до тех пор, пока все значения словаря неизменяемы

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

Довольно простой вариант:

 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 

Это реализация с небольшим объемом памяти, ценой не столь компактной, как и остальная.

 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 

вывод:

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

Это решение, которое я нашел:

 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 

В основном вы проверяете, присутствует ли идентификатор в списке, если он есть, удалите словарь, если нет, добавьте идентификатор в список

Быстрое и грязное решение – это просто создание нового списка.

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

Расширение на John La Rooy ( Python – список уникальных словарей ) отвечает, делая его немного более гибким:

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

Функция вызова:

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