Python-唯一词典列表


158

假设我有一个字典列表:

[
    {'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中实现这一目标?


5
这些词典有多广泛?您是否需要进行单个属性检查以确定重复项,或者检查单个值是否足够?
gddc 2012年

这些字典有8个key:value对,列表有200个字典。他们实际上有一个ID,如果找到的ID值是重复的,我可以安全地从列表中删除字典。
Limaaf 2012年


Answers:


238

因此,以密钥为临时做出命令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'}]

@John La Rooy-如何使用它基于多个属性从列表中删除字典,尝试了此方法,但似乎不起作用> {v ['flight'] ['lon'] ['lat']:v for v in stream} .values()
Jorge Vidinha

1
@JorgeVidinha假定每个都可以强制转换为str(或unicode),请尝试以下操作:{str(v['flight'])+':'+str(v['lon'])+','+str(v['lat']): v for v in stream}.values()这只是根据您的值创建一个唯一的键。赞'MH370:-21.474370,86.325589'
whunterknight,2016年

4
@JorgeVidinha,您可以使用元组作为字典键{(v['flight'], v['lon'], v['lat']): v for v in stream}.values()
John La Rooy 16/12/22

请注意,这可能会更改列表中词典的顺序!使用OrderedDict来自collections list(OrderedDict((v['id'], v) for v in L).values()) 或排序结果列表是否适合你更好
gevra

如果您需要考虑所有值,而不仅是ID,那么可以list({str(i):i for i in L}.values())在这里使用str(i)创建一个唯一的字符串,该字符串表示用于过滤重复项的字典。
–'DelboyJay

79

在集合中查找常见元素的通常方法是使用Python的set类。只需将所有元素添加到集合中,然后将集合转换为,然后list重复就消失了。

当然,问题在于a set()只能包含可哈希的条目,而a dict不可哈希。

如果遇到此问题,我的解决方案是将每个dict字符串转换为表示的字符串dict,然后将所有字符串添加至,然后将set()字符串值读出为,list()然后转换回dict

dictJSON格式很好地表示了字符串形式。而且Python有一个内置的JSON模块(json当然也称为)。

剩下的问题是,中的元素dict没有排序,并且当Python将转换dict为JSON字符串时,您可能会得到两个JSON字符串,它们表示等效字典,但不是相同的字符串。一种简单的解决方案是在调用sort_keys=True时传递参数json.dumps()

编辑:此解决方案是假设给定的dict任何部分都可以不同。如果我们可以假设dict具有相同"id"值的每个对象都将dict具有相同"id"值的其他对象匹配,那么这太过分了。@gnibbler的解决方案将更快,更轻松。

编辑:现在,安德烈·利马(AndréLima)有一条评论明确指出,如果ID是重复的,则可以安全地假定整个dict重复。因此,此答案过于刻薄,我建议使用@gnibbler的答案。


感谢您的帮助史蒂夫。您的回答实际上给了我一些我没有的知识,因为我刚开始使用Python =)
Limaaf 2012年

1
尽管在这种特殊情况下给定了过分的ID,但这仍然是一个很好的答案!
Josh Werts 2013年

8
这对我有帮助,因为我的字典没有键,并且仅由其所有条目唯一标识。谢谢!
ericso 2014年

该解决方案在大多数情况下都有效,但是在扩展时可能会出现性能问题,但是笔者我知道这一点,因此建议使用“ id”作为解决方案。性能问题:此解决方案使用序列化到字符串然后进行反序列化...序列化/反序列化是昂贵的计算,通常无法很好地扩展(项数为n> 1e6或每个字典包含> 1e6项或两者兼有),或者如果您有执行多次> 1e6或经常执行。
Trevor Boyd Smith,

正如简短地说,此解决方案说明了一个为什么要设计解决方案的典型示例……即,如果您具有唯一的ID……那么您可以有效地访问数据……如果您很懒且没有ID,则您的数据访问费用更高。
Trevor Boyd Smith,

21

如果字典仅由所有项目唯一标识(ID不可用),则可以使用JSON答案。以下是不使用JSON的替代方法,只要所有字典值都是不可变的,就可以使用

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

19

您可以使用numpy库(仅适用于Python2.x):

   import numpy as np 

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

要使其与Python 3.x(以及numpy的最新版本)一起使用,您需要将dict数组转换为numpy字符串数组,例如

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

13
TypeError: unorderable types: dict() > dict()在Python 3.5中执行此操作时获取错误。
Guillochon

16

这是一个相当紧凑的解决方案,尽管我怀疑这不是特别有效(说得有点客气):

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

3
map()list()Python 3 围绕该调用以返回列表,否则为map对象。
dmn

python 3.6+中此方法的另一个好处是保留了列表顺序
jnnnnn19年

7

由于id足以检测重复项,并且id可以进行哈希处理:请通过以id为主键的字典运行'em 。每个键的值是原始字典。

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 as键开头。


6
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,1,name':'john'},{'age':30,'id':2,2,'name':'hanna'}]


在同一示例中。如何获取仅包含相似ID的字典?
user8162 '16

@ user8162,您希望输出是什么样?
Yusuf X

有时,我会有相同的ID,但年龄不同。因此输出为[{'age':[34,40],'id':1,1,'name':['john',Peter]}]。简而言之,如果ID相同,则将其他内容组合到列表中,就像我在这里提到的那样。提前致谢。
user8162 '16

1
b = {x ['id']:[如果a中的x的y ['id'] == x ['id']]中的y,则是将它们组合在一起的一种方法。
Yusuf X

4

扩展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'])

4

我们可以做 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名字从john更改为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'}]

2

在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+有效,因为默认情况下字典是有序的。


1
在转储为JSON之前,请记住对键进行排序。你也不需要转换为list做之前set
弥敦道

2

我总结了我的最爱以尝试:

https://repl.it/@SmaMa/Python-List-of-unique-dictionaries

# ----------------------------------------------
# Setup
# ----------------------------------------------

myList = [
  {"id":"1", "lala": "value_1"},
  {"id": "2", "lala": "value_2"}, 
  {"id": "2", "lala": "value_2"}, 
  {"id": "3", "lala": "value_3"}
]
print("myList:", myList)

# -----------------------------------------------
# Option 1 if objects has an unique identifier
# -----------------------------------------------

myUniqueList = list({myObject['id']:myObject for myObject in myList}.values())
print("myUniqueList:", myUniqueList)

# -----------------------------------------------
# Option 2 if uniquely identified by whole object
# -----------------------------------------------

myUniqueSet = [dict(s) for s in set(frozenset(myObject.items()) for myObject in myList)]
print("myUniqueSet:", myUniqueSet)

# -----------------------------------------------
# Option 3 for hashable objects (not dicts)
# -----------------------------------------------

myHashableObjects = list(set(["1", "2", "2", "3"]))
print("myHashAbleList:", myHashableObjects)

1

快速而又肮脏的解决方案只是生成一个新列表。

sortedlist = []

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

1

我不知道您是否只希望列表中的字典ID是唯一的,但是如果目标是要有一组dict,其中所有键的值都具有唯一性,那么您应该使用元组键在您的理解中:

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

希望它可以帮助您或其他有问题的人。


1

这里有很多答案,所以让我添加另一个:

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

0

那么这里提到的所有答案都是好的,但是在某些答案中,如果字典项具有嵌套列表或字典,则可能会遇到错误,因此我提出了一个简单的答案

a = [str(i) for i in a]
a = list(set(a))
a = [eval(i) for i in a]

-1

这是一种内存开销很小的实现,但其代价是没有其余的那么紧凑。

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

1
您需要对此进行更多测试。修改表,而你是遍历它可能并不总是工作像您期望的
约翰·拉ROOY

@gnibbler很好!我将删除答案并对其进行更彻底的测试。
Samy Vilar 2012年

看起来更好。您可以使用集合来跟踪ID而不是字典。考虑开始indexlen(values)和倒计数,这意味着你可以随时递减index不管你del与否。例如for index in reversed(range(len(values))):
John La Rooy 2012年

@gnibbler有趣,集合是否几乎像字典一样看起来恒定?
Samy Vilar 2012年

-4

这是我发现的解决方案:

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

基本上,您检查ID是否存在于列表中,如果存在,则删除字典,否则,将ID附加到列表中


我将使用set而不是list作为usedID。这是一个更快的查找,并更具可读性
happydave

是的,我不知道布景...但是我正在学习...我只是在看@gnibbler答案...
tabchas 2012年

1
您需要对此进行更多测试。遍历列表时修改列表可能并不总是如您
所愿

是的,我不明白为什么它不起作用...有什么想法我做错了吗?
tabchas 2012年

不,我发现了问题...只是我不明白为什么会导致这个问题...您知道吗?
tabchas 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.