从列表中删除重复的子列表

5
如果我有这样一个列表:
mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]

最佳的去除重复子列表的方式是什么?

现在我使用这个方法

y, s = [ ], set( )
for t in mylist:
    w = tuple( sorted( t ) )
    if not w in s:
        y.append( t )
        s.add( w )

这个方法可以运行,但我想知道是否有更好的方法?是否有更符合Python风格的方法?


我不想改变顺序。 - MarioBross
那么icodez就是你应该使用的答案,[1,2][2,1]是重复的吗? - Padraic Cunningham
你的接受答案不保持顺序,集合没有任何顺序。 - Padraic Cunningham
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Evpok
7个回答

10
将元素转换为元组*, 然后将整个东西转换为集合,最后将所有内容都转换回列表:
将元素转换为元组*,然后将整个对象转换为 set 类型,最后将结果转换回 list 类型:
m = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]

print [list(i) for i in set(map(tuple, m))]

*我们正在将列表转换为元组,因为列表是不可哈希的(因此无法在其上使用集合)。


1
谢谢。它按照我想要的方式工作。 - MarioBross

8
您可以使用 OrderedDict.fromkeys 来过滤掉列表中的重复项,同时仍保留顺序:
>>> from collections import OrderedDict
>>> mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]
>>> map(list, OrderedDict.fromkeys(map(tuple, mylist)))
[[1, 2, 3], ['a', 'c'], [3, 4, 5], [1, 2]]
>>>

map(tuple, mylist)是必需的,因为字典键必须是可哈希的(列表不是因为您可以添加/删除其中的项)。


5

嗯,既然set本身就可以去重,你的第一反应可能是使用set(mylist)。但是,这并不完全有效:

In [1]: mylist = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]

In [2]: set(mylist)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-2-b352bcae5975> in <module>()
----> 1 set(mylist)

TypeError: unhashable type: 'list'

这是因为 `set` 只能用于可哈希元素的 `iterable`(由于 `list` 是可变的,所以它们不可哈希)。
相反,你可以通过将子列表转换为子元组来简单地完成此操作。
In [3]: set([tuple(x) for x in mylist])
Out[3]: {(1, 2), (1, 2, 3), (3, 4, 5), ('a', 'c')}

或者,如果你真的需要再次列出列表:

In [4]: [list(x) for x in set([tuple(x) for x in mylist])]
Out[4]: [[1, 2], [3, 4, 5], ['a', 'c'], [1, 2, 3]]

1
有人可以解释一下为什么要踩这个回答吗?因为它明显解决了提问者所述的问题。 - user554546
1
我给你们点赞以抵消那个踩赞。说真的,那是怎么回事? - Secret

4

因为你在问题中使用了sorted(t),我假设你认为[1,2][2,1]是重复的。

如果这是真的,我会在内部列表中使用frozenset(可哈希),并且不会关心子列表的顺序。

所以可以像这样:

set(frozenset(sublist) for sublist in mylist)

代码来自另一个问题,排序无关紧要。 - Padraic Cunningham

2
你不需要排序,你复制的代码中的排序是为了不同的原因而进行的:
seen,out = set(), []

for ele in mylist:
    tp = tuple(ele)
    if tp not in seen:
        out.append(ele)
    seen.add(tp)

1
这将适用于您的情况:

好的


mylist2 = set(map(tuple, mylist))
print(mylist2) # ('a', 'c'), (3, 4, 5), (1, 2), (1, 2, 3)}

这段代码可以正常运行,因为它将你的子列表转换为元组,而元组在你的情况下是可哈希的。因此,集合(set)可以接受它们并使其成为唯一的。
如果你真的想要输出一个列表的列表,你可以这样做:
print(list(map(list,mylist2))) # [['a', 'c'], [3, 4, 5], [1, 2], [1, 2, 3]]

我为你们点赞以抵消那个踩票。说真的,那是怎么回事? - Secret
@Secret 谢谢。有些人似乎没有什么明显的理由而下载。 - Marcin
1
可能是因为你得到的结果是一组元组,而不是一组列表。 - Evpok
1
OP的代码还创建了一组元组,所以我猜这对于OP来说不是问题。 - Marcin
OP创建元组以添加到集合中,而不是列表。 - Padraic Cunningham

1
如果顺序和结构(列表的列表)不重要,您可以使用
set(map(tuple, my_list))

如果它们很重要,您可以使用列表推导式。
[e for i,e in enumerate(my_list) if e not in my_list[:i]]

该函数仅保留每个元素的第一个重复项,因此仅保留其中一个。它的速度略慢。

In [16]: timeit.timeit('[e for i,e in enumerate(my_list) if e not in my_list[:i]]', setup="my_list = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]")
Out[16]: 1.9146944019994407

In [17]: timeit.timeit('set(map(tuple, my_list))', setup="my_list = [[1,2,3], ['a', 'c'], [3,4,5],[1,2], [3,4,5], ['a', 'c'], [3,4,5], [1,2]]")
Out[17]: 1.3857673469974543

但如果您关心速度,您应该尝试一种循环方法。

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