在Python字典列表中查找重复项

14

我下面有一个字典:

a = [{'un': 'a', 'id': "cd"}, {'un': 'b', 'id': "cd"},{'un': 'b', 'id':    "cd"}, {'un': 'c', 'id': "vd"},
    {'un': 'c', 'id': "a"}, {'un': 'c', 'id': "vd"}, {'un': 'a', 'id': "cm"}]

我需要通过“un”键找到字典的重复项,例如这个 {'un': 'a', 'id': "cd"} 和这个 {'un': 'a', 'id': "cm"} 字典由于键'un'的值相同而被视为重复项;第二步是在找到重复项后,根据键'id'的第二个值来决定应该保留哪一个字典,例如我们保留pattern值为"cm"的字典。

我已经完成了第一步,请参见下面的代码:

from collections import defaultdict
temp_ids = []
dup_dict = defaultdict(list)
for number, row  in enumerate(a):
    id = row['un']
    if id not in temp_ids:
        temp_ids.append(id)
    else:
        tally[id].append(number)

使用这段代码,我大致能够找到重复列表的索引,也许有其他方法可以做到。而且我需要下一步的代码来决定保留哪个字典和省略哪个字典。感谢您的帮助。


你需要使用一个字典列表吗? 对于这种任务,数据框可能更适合。 - C_Z_
你是在询问查找键本身的重复还是['un']值的重复? - Aaron
值的重复 - Yan
我正在以字典列表的形式获取这些数据,也许将来会尝试。谢谢建议! - Yan
dict 是 Python 内置的用于创建字典的命令,因此您可能要避免将其用作变量名。 - Chris Mueller
谢谢,我已经重构了它。 - Yan
3个回答

7

之前的答案对于字典项超过两个的列表(List)效果不佳(即它们只保留最多两个键值对 - 如果想要保留所有键值对,但删除其中一个特定键重复的怎么办?)

为了避免向存在重复特定键的字典列表(List of Dicts)中添加新项,可以这样做:

import pandas as pd

all = [
    {"email":"art@art.com", "dn":"Art", "pid":11293849},
    {"email":"bob@bob.com", "dn":"Bob", "pid":12973129},
    {"email":"art@art.com", "dn":"Art", "pid":43975349},
    {"email":"sam@sam.com", "dn":"Sam", "pid":92379234},
]

df = pd.DataFrame(all)
df.drop_duplicates(subset=['email'], keep='last', inplace=True)
all = df.to_dict("records")
print(all)

这应该是最有价值的答案,因为它考虑了兼容性,并且对于遇到类似问题的其他读者非常有用。 - harmonica141

4

一般而言,如果您想在字典列表中查找重复项,您应该按照一种方式对字典进行分类,使得重复项保留在同一组中。为了实现这一目的,您需要基于 dict 项目进行分类。由于对于字典,顺序并不重要,因此您需要使用一个既可散列又不保留容器顺序的容器。最好的选择是使用 frozenset()

示例:

In [87]: lst = [{2: 4, 6: 0},{20: 41, 60: 88},{5: 10, 2: 4, 6: 0},{20: 41, 60: 88},{2: 4, 6: 0}]

In [88]: result = defaultdict(list)

In [89]: for i, d in enumerate(lst):
    ...:     result[frozenset(d.items())].append(i)
    ...:     
In [91]: result
Out[91]: 
defaultdict(list,
            {frozenset({(2, 4), (6, 0)}): [0, 4],
             frozenset({(20, 41), (60, 88)}): [1, 3],
             frozenset({(2, 4), (5, 10), (6, 0)}): [2]})

在这种情况下,您可以根据 'un' 键对字典进行分类,然后根据 id 选择所需的项:

>>> from collections import defaultdict
>>> 
>>> d = defaultdict(list)
>>> 
>>> for i in a:
...     d[i['un']].append(i)
... 
>>> d
defaultdict(<type 'list'>, {'a': [{'un': 'a', 'id': 'cd'}, {'un': 'a', 'id': 'cm'}], 'c': [{'un': 'c', 'id': 'vd'}, {'un': 'c', 'id': 'a'}, {'un': 'c', 'id': 'vd'}], 'b': [{'un': 'b', 'id': 'cd'}, {'un': 'b', 'id': 'cd'}]})
>>> 
>>> keeps = {'a': 'cm', 'b':'cd', 'c':'vd'} # the key is 'un' and the value is 'id' should be keep for that 'un'
>>> 
>>> [i for key, val in d.items() for i in val if i['id']==keeps[key]]
[{'un': 'a', 'id': 'cm'}, {'un': 'c', 'id': 'vd'}, {'un': 'c', 'id': 'vd'}, {'un': 'b', 'id': 'cd'}, {'un': 'b', 'id': 'cd'}]
>>> 

在最后一行(嵌套的列表推导式)中,我们首先循环遍历聚合字典的项,然后遍历值,并保留那些在值之后的项目或条件中的项,即i['id']==keeps[key]表示我们将保留具有指定值的keeps字典中id的项。
您可以将列表推导式分解为以下内容:
final_list = []
for key, val in d.items():
    for i in val:
        if i['id']==keeps[key]:
             final_list.append(i)

请注意,由于列表推导式的迭代是在C中执行的,因此它比常规的Python循环更快,并且是Pythonic的方式。但是,如果性能对您不重要,则可以使用常规方法。


你能简要解释一下你最后的一行代码吗? - Yan

2

您基本上已经正确地使用了 defaultdict... 这是我大致的写法。

from collections import defaultdict
a = [{'un': 'a', 'id': "cd"}, {'un': 'b', 'id': "cd"},{'un': 'b', 'id':    "cd"}, {'un': 'c', 'id': "vd"}, {'un': 'c', 'id': "a"}, {'un': 'c', 'id': "vd"}, {'un': 'a', 'id': "cm"}]

items = defaultdict(list)
for row in a:
    items[row['un']].append(row['id'])  #make a list of 'id' values for each 'un' key

for key in items.keys():
    if len(items[key]) > 1:  #if there is more than one 'id'
        newValue = somefunc(items[key])  #decided which of the list items to keep
        items[key] = newValue  #put that new value back into the dictionary

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接