从列表中的嵌套字典中删除重复项

4

快速而基础的新手问题。

如果我有一个字典列表,长这样:

L = []
L.append({"value1": value1, "value2": value2, "value3": value3, "value4": value4})

假设存在多个条目,其中value3和value4与其他嵌套字典相同。如何快速轻松地查找和删除这些重复的字典。

保留顺序并不重要。

谢谢。

编辑:

如果有五个输入,像这样:

L = [{"value1": fssd, "value2": dsfds, "value3": abcd, "value4": gk},
    {"value1": asdasd, "value2": asdas, "value3": dafdd, "value4": sdfsdf},
    {"value1": sdfsf, "value2": sdfsdf, "value3": abcd, "value4": gk},
    {"value1": asddas, "value2": asdsa, "value3": abcd, "value4": gk},
    {"value1": asdasd, "value2": dskksks, "value3": ldlsld, "value4": sdlsld}]

输出应该像这样:
L = [{"value1": fssd, "value2": dsfds, "value3": abcd, "value4": gk},
    {"value1": asdasd, "value2": asdas, "value3": dafdd, "value4": sdfsdf},
    {"value1": asdasd, "value2": dskksks, "value3": ldlsld, "value4": sdlsld}

澄清一下,您想要删除键/值对,如果在另一个字典中存在匹配的键/值对,还是只要在另一个字典中存在该键(不一定需要值)就可以? - Kenan Banks
只有key3和key4不能相同吗?如果一个键的值与另一个字典中的另一个键的值相匹配,会发生什么?另外,顺便说一下,请给你的列表取一个不同于list的名字,否则会覆盖内置命名空间中的实际list,并且以后无法调用list()函数。lstlist_是相当常见的替代选项。 - Nikhil
是的,只需要key3和key4,其余可以重复。 - Jonas
我只是在列表中使用字典,因为这比在列表中使用另一个列表更容易理解和操作。这样你就可以通过l["value1"]来调用它了,但这是另外一个故事了。 - Jonas
现在你有一个列表,其中每个都包含一个字典。你确定要在每个字典周围加上额外的 [ ] 吗? - Ned Deily
6个回答

7

以下是一种方法:

keyfunc = lambda d: (d['value3'], d['value4'])

from itertools import groupby
giter = groupby(sorted(L, key=keyfunc), keyfunc)

L2 = [g[1].next() for g in giter]
print L2

1
看起来你的是正确的,比Alex的早一个小时。 - hughdbrown
1
我猜一旦一个问题得到5或6个以上的答案,很容易被忽视。我认为处于前几个或后几个可能会有所帮助。这不是什么大事,但感谢您注意到了这一点。 :) - ars
2
在Python3.3中运行此代码时,出现错误AttributeError: 'itertools._grouper' object has no attribute 'next'。有什么线索吗? - lukik

7

在Python 2.6或3.*中:

import itertools
import pprint

L = [{"value1": "fssd", "value2": "dsfds", "value3": "abcd", "value4": "gk"},
    {"value1": "asdasd", "value2": "asdas", "value3": "dafdd", "value4": "sdfsdf"},
    {"value1": "sdfsf", "value2": "sdfsdf", "value3": "abcd", "value4": "gk"},
    {"value1": "asddas", "value2": "asdsa", "value3": "abcd", "value4": "gk"},
    {"value1": "asdasd", "value2": "dskksks", "value3": "ldlsld", "value4": "sdlsld"}]

getvals = operator.itemgetter('value3', 'value4')

L.sort(key=getvals)

result = []
for k, g in itertools.groupby(L, getvals):
    result.append(next(g))

L[:] = result
pprint.pprint(L)

在Python 2.5中几乎相同,只是在添加中需要使用g.next()而不是next(g)

2
你可以使用一个临时数组来存储项目字典。之前的代码在for循环中移除项目是有错误的。
(v,r) = ([],[])
for i in l:
    if ('value4', i['value4']) not in v and ('value3', i['value3']) not in v:
        r.append(i)
    v.extend(i.items())
l = r

您的测试:

l = [{"value1": 'fssd', "value2": 'dsfds', "value3": 'abcd', "value4": 'gk'},
    {"value1": 'asdasd', "value2": 'asdas', "value3": 'dafdd', "value4": 'sdfsdf'},
    {"value1": 'sdfsf', "value2": 'sdfsdf', "value3": 'abcd', "value4": 'gk'},
    {"value1": 'asddas', "value2": 'asdsa', "value3": 'abcd', "value4": 'gk'},
    {"value1": 'asdasd', "value2": 'dskksks', "value3": 'ldlsld', "value4": 'sdlsld'}]

输出

{'value4': 'gk', 'value3': 'abcd', 'value2': 'dsfds', 'value1': 'fssd'}
{'value4': 'sdfsdf', 'value3': 'dafdd', 'value2': 'asdas', 'value1': 'asdasd'}
{'value4': 'sdlsld', 'value3': 'ldlsld', 'value2': 'dskksks', 'value1': 'asdasd'}

你的输出不正确。看看我的例子。无论如何,还是感谢你的尝试。 - Jonas

1
for dic in list: 
  for anotherdic in list:
    if dic != anotherdic:
      if dic["value3"] == anotherdic["value3"] or dic["value4"] == anotherdic["value4"]:
        list.remove(anotherdic)

测试通过

list = [{"value1": 'fssd', "value2": 'dsfds', "value3": 'abcd', "value4": 'gk'},
{"value1": 'asdasd', "value2": 'asdas', "value3": 'dafdd', "value4": 'sdfsdf'},
{"value1": 'sdfsf', "value2": 'sdfsdf', "value3": 'abcd', "value4": 'gk'},
{"value1": 'asddas', "value2": 'asdsa', "value3": 'abcd', "value4": 'gk'},
{"value1": 'asdasd', "value2": 'dskksks', "value3": 'ldlsld', "value4": 'sdlsld'}]

对我来说运行得很好 :)


1

这是一个包含一个字典的列表,但是假设列表l中还有更多的字典:

l = [ldict for ldict in l if ldict.get("value3") != value3 or ldict.get("value4") != value4]

但这真的是你想做的吗?也许你需要精炼你的描述。

顺便说一下,不要使用list作为名称,因为它是Python内置函数的名称。

编辑:假设您从字典列表开始,而不是每个列表中只有1个字典的列表,那么应该可以使用您的示例。如果任何一个值为None,则无法正常工作,因此最好使用类似于:

l = [ldict for ldict in l if not ( ("value3" in ldict and ldict["value3"] == value3) and ("value4" in ldict and ldict["value4"] == value4) )]

但它仍然看起来像一个不寻常的数据结构。

编辑:无需使用显式的get

此外,解决方案总是存在折衷。没有更多信息,也没有实际测量,很难知道哪些性能折衷对于该问题最重要。但是,正如禅语所说:“简单比复杂好。”


你好 Ned,感谢你的建议,我已经添加了一个关于同一列表的输入和输出示例,并且在该特定示例中已经重命名了该列表。谢谢。 - Jonas

0

如果我理解正确,您想要丢弃原始列表中后面出现的匹配项,但不关心生成的列表的顺序,因此:

(已在2.5.2版本进行了测试)

tempDict = {}
for d in L[::-1]:
    tempDict[(d["value3"],d["value4"])] = d
L[:] = tempDict.itervalues()
tempDict = None

你尝试运行代码了吗?它没有按照OP的要求执行。有几个问题:(1)为什么要倒序迭代列表?(2)为什么在临时字典中使用(d["value3"],d["value4"])作为键?(3)为什么在迭代过程中将列表中的当前字典分配为临时字典的值? - hughdbrown
嗯 - 我的解释是对的(我不确定),并且也匹配他的输出 - 虽然顺序不同,但他说保留顺序并不重要。我的解释是:当存在多个具有相同(value3、value4)对的字典时,请仅保留原始列表中第一个这样的字典。结果字典列表可以不按相同的顺序排列。因此... (1) 所以原始列表中的第一个实例将“获胜”并被保留,(2) 因为我认为那才是唯一的,(3) 因为这些字典是我从新列表中提取出来的值。 - Anon
在我的测试输出中,字典项以相反的顺序打印出来,而字典列表则按不同的顺序排列,但由于他说“保留顺序并不重要”,因此这似乎在参数范围内。 - Anon
回顾一切,我坚持我的解释。秩序似乎是唯一的争议点。请注意,如果OP的原始数据将“abcd”的实例替换为“xkcd”,则Alex答案中的排序(像往常一样)也会导致不同的顺序。问题的随机外观(甚至没有引用)的数据没有表明它的顺序是任何其他偶然事件 - 再次特别结合“保留顺序并不重要”。 - Anon

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