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
В основном вы проверяете, присутствует ли идентификатор в списке, если он есть, удалите словарь, если нет, добавьте идентификатор в список