在Python中删除列表中的重复列表

4

我看到这里有一些相关的问题,但它们的答案对我不起作用。我有一个列表,其中一些子列表重复,但它们的元素可能是无序的。例如

g = [[1, 2, 3], [3, 2, 1], [1, 3, 2], [9, 0, 1], [4, 3, 2]]

根据我的问题,输出应该自然而然地是:

g = [[1,2,3],[9,0,1],[4,3,2]]

我尝试使用set,但只删除那些相等的列表(我认为它应该工作,因为集合在定义上没有顺序)。其他我访问过的问题只有完全重复或重复的列表示例,如此Python: 如何从列表中删除重复的列表?。目前,输出的顺序(对于列表和子列表)不是一个问题。


你的答案很棒!我正在寻找哪个答案在更大的列表中表现最好。 - Alejandro Sazo
4个回答

7

滥用副作用的列表推导式版本:

seen = set()

[x for x in g if frozenset(x) not in seen and not seen.add(frozenset(x))]
Out[4]: [[1, 2, 3], [9, 0, 1], [4, 3, 2]]

对于那些(不像我自己)不喜欢以这种方式使用副作用的人:

res = []
seen = set()

for x in g:
    x_set = frozenset(x)
    if x_set not in seen:
        res.append(x)
        seen.add(x_set)

你将frozenset添加到集合中的原因是因为只有可哈希对象才能添加到set中,而普通的set是不可哈希的。

你的回答是最快的。而且奇怪的是,它给出了我想要的输出,但我并没有要求。它比@jterrace的回答快两倍。对于一个包含4205个子列表的列表,你的回答只用了0.02秒。 - Alejandro Sazo
@AlejandroSazo请检查一下我的答案性能,其中包含生成器表达式:g = [list(x) for x in set(frozenset(i) for i in (set(i) for i in g))] 我只是想知道它的基准测试,并且很乐意看到结果 :) - andilabs
1
@andi 我会尽快完成。 - Alejandro Sazo

3

如果您不关心列表和子列表的顺序(并且所有子列表中的项目都是唯一的):

result = set(map(frozenset, g))

如果子列表中可能有重复项,例如[1, 2, 1, 3],则可以使用tuple(sorted(sublist))替代frozenset(sublist)来去除该子列表中的重复项。
如果要保留子列表的顺序:
def del_dups(seq, key=frozenset):
    seen = {}
    pos = 0
    for item in seq:
        if key(item) not in seen:
            seen[key(item)] = True
            seq[pos] = item
            pos += 1
    del seq[pos:]

例子:

del_dups(g, key=lambda x: tuple(sorted(x)))

请查看在Python中,如何最快地从列表中删除重复项,同时保持顺序?


1
不错的想法!只需添加 [list(x) for x in set(map(frozenset, g))] 以按照 OP 的要求生成输出。 - andilabs
2
@andi:我将“result”设为一组“frozenset”,以强调所有子列表和每个子列表中的所有项都是唯一且顺序不重要。 - jfs

1
我会将列表中的每个元素转换为不可变集合(frozenset),这样就可以进行哈希处理,然后创建一个新的集合以去除重复项:
>>> g = [[1, 2, 3], [3, 2, 1], [1, 3, 2], [9, 0, 1], [4, 3, 2]]
>>> set(map(frozenset, g))
set([frozenset([0, 9, 1]), frozenset([1, 2, 3]), frozenset([2, 3, 4])])

如果您需要将元素转换回列表:
>>> map(list, set(map(frozenset, g)))
[[0, 9, 1], [1, 2, 3], [2, 3, 4]]

这个答案似乎非常快,而且返回的元组是有序的。只是缺少了列表返回 :P - Alejandro Sazo
@AlejandroSazo 请查看我的更新答案。我认为它比你接受的答案更快更简单。 - jterrace
我会尽快完成! - Alejandro Sazo

1

使用roippi提到的frozenset,可以这样做:

>>> g = [list(x) for x in set(frozenset(i) for i in [set(i) for i in g])]

[[0, 9, 1], [1, 2, 3], [2, 3, 4]]

如果性能很重要,您可以将列表推导式替换为生成器表达式。只需用()替换[]即可:g = [list(x) for x in set(frozenset(i) for i in (set(i) for i in g))] 您可以在此处阅读区别:https://dev59.com/AHVD5IYBdhLWcg3wOo9h - andilabs
1
你的第一种方法(列表推导式)用了0.070491秒。使用生成器表达式的方法只花费了0.030556秒。被接受的答案时间是0.02秒 :) - Alejandro Sazo

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