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?

Ответ 1

Итак, создайте временный 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'}]

Ответ 2

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

Проблема, конечно, в том, что a set() может содержать только хешируемые записи, a 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.

Ответ 3

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

   import numpy as np 

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

Чтобы это работало с Python 3.x (и последними версиями numpy), вам нужно преобразовать массив dicts в numpy массив строк, например

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

Ответ 4

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

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

Ответ 5

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

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

Ответ 6

Так как 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 в качестве начала.

Ответ 7

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

Ответ 8

Расширение на 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'])

Ответ 9

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

sortedlist = []

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

Ответ 10

В Python 3. 6+ (что я тестировал), просто используйте:

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)

Объяснение: мы отображаем json.dumps для кодирования словарей как объектов json, которые являются неизменяемыми. Затем set можно использовать для создания итерируемой уникальной неизменяемой переменной. Наконец, мы преобразуем обратно в наше словарное представление, используя json.loads. Обратите внимание, что изначально нужно сортировать по ключам, чтобы словари располагались в уникальной форме. Это действительно для Python 3. 6+, так как словари упорядочены по умолчанию.

Ответ 11

Мы можем сделать с 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'}]

Обратите внимание, немного отличается от принять ответ.

drop_duplicates проверит все столбцы в пандах, если все одинаковые, то строка будет удалена.

Например:

Если мы изменим имя второго dict с Джона на Питера

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

Ответ 12

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

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

Ответ 13

Я не знаю, хотите ли вы, чтобы идентификатор ваших dicts только в списке был уникальным, но если цель состоит в том, чтобы иметь набор dict, в котором уникальность находится на значениях всех ключей... вы должны использовать кортежи key следующим образом в вашем понимании:

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

Надеюсь, это поможет вам или другому человеку, имеющему проблемы....

Ответ 14

Здесь много ответов, поэтому позвольте мне добавить еще один:

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)

Ответ 15

Это реализация с небольшими накладными расходами памяти за счет того, что они не были такими компактными, как остальные.

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

Ответ 16

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

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

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