Вопрос по python, dictionary – Python - список уникальных словарей

110

Допустим, я получил список словарей:

[
    {'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?

Эти дикты получили 8 пар ключ: значение, а список получил 200 диктов. Они на самом деле получили идентификатор, и я могу удалить диктат из списка, если найденное значение идентификатора является дубликатом. Limaaf
Возможный дубликатHow to make values in list of dictionary unique? Abhijeet
forzenset это эффективный вариант.set(frozenset(i.items()) for i in list) Abhijeet
Насколько обширны эти словари? Вам нужна индивидуальная проверка атрибутов для определения дубликатов, или достаточно проверки одного значения в них? g.d.d.c

Ваш Ответ

15   ответов
0

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

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'}]
@gnibbler очень хорошая мысль! Я удалю ответ и протестирую его более тщательно.
Выглядит лучше. Вы можете использовать набор для отслеживания идентификаторов вместо dict. Рассмотреть вопрос о началеindex вlen(values) и считая в обратном направлении, это означает, что вы всегда можете уменьшитьindex ли тыdel или нет. напримерfor index in reversed(range(len(values))):
@gnibbler интересно, наборы имеют почти постоянный вид словари?
Вы должны проверить это немного больше. Изменение списка во время итерации может не всегда работать так, как вы ожидаете
@gnibbler это лучше?
61

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

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

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

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

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

РЕДАКТИРОВАТЬ: Это решение предполагало, что данныйdict может иметь любую другую часть. Если мы можем предположить, что каждыйdict с тем же"id" значение будет соответствовать каждому другомуdict с тем же"id" значение, то это излишне; Решение @ gnibbler будет быстрее и проще.

РЕДАКТИРОВАТЬ: Теперь есть комментарий от Andr & # xE9; Лима прямо говорит, что если идентификатор является дубликатом, можно с уверенностью предположить, что весьdict это дубликат. Так что этот ответ излишний, и я рекомендую ответ @ gnibbler.

Несмотря на то, что в данном конкретном случае было получено избыточное количество идентификаторов, это все же отличный ответ!
Спасибо за помощь стевеха. Ваш ответ фактически дал мне некоторые знания, которых у меня не было, так как я только начал с Python =) Limaaf
Это помогает мне, так как мой словарь не имеет ключа, а только однозначно идентифицируется всеми его записями. Спасибо!
0

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

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

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

172

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

В 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'}]
обратите внимание, что это может изменить порядок словарей в списке! использованиеOrderedDict отcollections list(OrderedDict((v['id'], v) for v in L).values())   или отсортируйте полученный список, если это работает лучше для вас
@JorgeVidinha, предполагая, что каждый может быть приведен к str (или unicode), попробуйте это:{str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values() Это просто создает уникальный ключ на основе ваших ценностей. подобно'MH370:-21.474370,86.325589'
@JorgeVidinha, вы можете использовать кортеж в качестве словарного ключа{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
@John La Rooy - как можно использовать одно и то же для удаления словарей из списка, основанного на нескольких атрибутах, пробовал это, но, похоже, не работает & gt; {v ['flight'] ['lon'] ['lat']: v для v в потоке} .values ()
Это сделало работу =) Практически все ответы верны и помогли мне. Отметить это как правильный для включения версий кода Python diff. Приветствую всех, кто помог мне. Limaaf
5
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'}]

b = {x ['id' ']: [y для y в a, если y [' id 'a]; == x [' id ']] для x в a}, - это один из способов их группировки.
В том же примере. как я могу получить дикты, содержащие только похожие идентификаторы?
Иногда у меня будет одинаковое удостоверение личности, но разного возраста. так что вывод будет [{'age': [34, 40], 'id': 1, 'name': ['john & apos ;, Peter]}]. Короче говоря, если идентификаторы одинаковы, то объедините содержимое других в список, как я упоминал здесь. Заранее спасибо.
@ user8162, как бы вы хотели, чтобы вывод выглядел?
0

Быстрое и грязное решение заключается в создании нового списка.

sortedlist = []

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

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

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

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

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
7

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

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

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

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

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

Кроме того, это может иметь смысл простоkeep данные в словаре, который используетid как ключ для начала.

2

Расширение на Джона Ла Руи (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'])
13

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

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

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

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)

Explanation: мы отображаемjson.dumps кодировать словари как объекты json, которые являются неизменными.set затем может быть использован для создания итерируемогоunique immutables. Наконец, мы преобразуем обратно в наше словарное представление, используяjson.loads.

-1

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

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

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

Попробуйте запустить свой код с 3 копиями идентификатора 1 подряд.
Да, я не понимаю, почему это не работает ... Есть идеи, что я делаю неправильно?
Вы должны проверить это немного больше. Изменение списка во время итерации может не всегда работать так, как вы ожидаете
Да, я не знал о наборах ... но я учусь ... Я просто смотрел на ответ @gnibbler ...
Я бы использовал набор, а не список для usedID. Это более быстрый поиск и более читаемый
18

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

   import numpy as np 

   list_of_unique_dicts=list(np.unique(np.array(list_of_dicts)))
Получить ошибкуTypeError: unorderable types: dict() > d,ict() при этом в Python 3.5.
12

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

>>> 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'}]
Окружитьmap() позвонить сlist() в Python 3, чтобы получить список обратно, в противном случае этоmap объект.

Похожие вопросы