从列表中删除重复的可变对象

Remove duplicate mutable objects from a list

提问人:joseville 提问时间:11/6/2022 更新时间:11/6/2022 访问量:111

问:

当我有一个不可变对象的列表,并且想要摆脱重复项时,我可以使用:lstset(lst)

lst = [0,4,2,6,3,6,4,9,2,2] # integers are immutable in python
print(set(lst)) # {0,2,3,4,6,9}

但是,假设我有一个可变对象列表,并且想要删除重复项。 不会起作用,因为可变对象不可散列 - 我们会得到一个 .在这种情况下,我们应该怎么做?lstset(lst)TypeError: unhashable type: '<type>'

例如,假设我们有 ,一个 s 列表(s 是可变的,因此不可散列),并且有些在 中多次出现:lstdictdictdictslst

d0 = {0:'a', 1:'b', 9:'j'}
d1 = {'jan':1, 'jul':7, 'dec':12}
d2 = {'hello':'hola', 'goodbye':'adios', 'happy':'feliz', 'sad':'triste'}
lst = [d0, d1, d1, d0, d2, d1, d0]

我们想遍历,但只考虑一次。如果我们这样做,我们会得到一个 .相反,我们必须做这样的事情:lstdictset(lst)TypeError: unhashable type: 'dict'

def dedup(lst):
  seen_ids = set()
  for elem in lst:
    id_ = id(elem)
    if id_ not in seen_ids:
      seen_ids.add(id_)
      yield elem

有没有更好的方法可以做到这一点???

重复 身份 可变

评论

0赞 zvone 11/6/2022
这要视情况而定。也可能有两个字典是相同的,但不是同一个实例,因此它们具有不同的 ID。你想用它们做什么?
0赞 zvone 11/6/2022
顺便说一句,为什么你甚至有一个词典列表,其中一些是同一本词典?我无法想象这是一个好主意的场景。
0赞 quamrana 11/6/2022
这个问题的答案有帮助吗?
0赞 mkrieger1 11/6/2022
如果 d1 和 d2 相等(但仍然具有不同的 id)会发生什么?根据这一点,这可能是一个重复的问题,即 stackoverflow.com/questions/11092511/......stackoverflow.com/questions/54964209/......
0赞 joseville 11/6/2022
@zvone LC695 的一次性、无 dfs/bfs 解决方案的失败解决方案:岛屿最大面积。这是失败的解决方案。用数字标记每个岛屿。如果在任何时候,事实证明两个岛屿实际上是相同的,那么我们就会“合并”它们。为此,请保留一个集合列表(集合是不可变的),其中 是岛屿 i 合并的岛屿集合。groupsgroups[i]

答:

1赞 chrslg 11/6/2022 #1

好吧,您可以使用带有 id 的 dict 作为键。这是大致相同的想法,但允许像这样的单行。

list({id(x):x for x in lst}.values())

评论

0赞 0x0fba 11/6/2022
如果将内容与 相同的字典添加到字典中,则不再起作用。lstd3d0
0赞 chrslg 11/6/2022
@fbattello确实如此。但在我看来,这似乎是这个问题的一个假设。我看到,在另一条评论中,您理解这个问题有深刻的比较。我没有,我承认不清楚。在最后的评论之后,我仍然不确定(它们似乎表明,我们确实希望根据其内容删除重复项。但甚至不确定这些是岛屿,还是索引列表或......但实际上,如果我们想从内容中删除重复项,您的答案就是其中之一(如果有循环或类似的东西,它将不起作用)。如果没有,那就太慢了。
-1赞 user3435121 11/6/2022 #2

此方法使用 2 个列表:ID 列表和元素列表。

d0 = {0:'a', 1:'b', 9:'j'}
d1 = {'jan':1, 'jul':7, 'dec':12}
d2 = {'hello':'hola', 'goodbye':'adios', 'happy':'feliz', 'sad':'triste'}
lst = [d0, d1, d1, d0, d2, d1, d0]

def dedup( lst):
  id_seen = []
  elems = []
  for elem in lst:
    if id(elem) not in id_seen:
      id_seen.append( id(elem))
      elems.append( elem)
  return elems

print( dedup( lst))

评论

2赞 mkrieger1 11/6/2022
与问题中已经显示的内容相比,这有什么改进?
3赞 0x0fba 11/6/2022 #3

使用对象 ID 非常危险。 如果您有两个内容相同的字典,则它不再起作用。 您可以使用该模块将字典序列化为字符串。json

import json

def dedup(lst):
    myset = {json.dumps(x) for x in lst}  # serialize to a set
    return [json.loads(x) for x in myset]  # deserialize to the list

评论

3赞 mkrieger1 11/6/2022
目前尚不清楚是否需要一种或另一种行为。
1赞 0x0fba 11/6/2022
根据问题,问题是为不可散列的对象(示例中的字典)找到解决方案。如果它们是可散列的,则具有相同内容的两个对象将具有相同的散列值,因此将相等。在我的理解中,首选的行为是,如果两个对象的内容相等,则解决方案必须将两个对象视为相等。
1赞 0x0fba 11/6/2022
d0 = {0:'a', 1:'b', 9:'j'} d3 = {0:'a', 1:'b', 9:'j'} id(d0) == id(d3) # False d0 == d3 # True