如何从一个列表中删除所有重复项

78

如何使用Python来检查并删除列表中的所有重复项?我不想指定要删除的重复项 - 我希望代码能够自动找出是否有任何重复项,如果有,则将它们删除,并只保留每个重复项的一个实例。如果列表中有多个重复项,它也必须正常工作。

例如,在下面的代码中,列表lseparatedOrbList有12个项 - 一个重复了六次,一个重复了五次,另一个则只出现了一次。我想要更改这个列表,使其只有三个项目 - 每个项目只出现一次,并且与之前出现的顺序相同。我尝试了以下代码:

for i in lseparatedOrbList:
   for j in lseparatedOrblist:
        if lseparatedOrbList[i] == lseparatedOrbList[j]:
            lseparatedOrbList.remove(lseparatedOrbList[j])

但是我遇到了这个错误:

Traceback (most recent call last):
  File "qchemOutputSearch.py", line 123, in <module>
    for j in lseparatedOrblist:
NameError: name 'lseparatedOrblist' is not defined

我猜是因为我试图在循环遍历lseparatedOrbList时对它进行了另一个循环遍历,但我想不出其他方法来做到这一点。


你需要维护列表的顺序吗? - Will McCutchen
一个常见的问题:http://stackoverflow.com/search?q=python+duplicates+list。 - S.Lott
2
你的错误是由于一个简单的打字错误引起的:在第二个for循环中,你没有将lseparatedOrbList的第二个“l”大写。 - ApproachingDarknessFish
11个回答

141

使用 set()

woduplicates = set(lseparatedOrblist)

返回一个不包含重复元素的集合。如果由于某些原因需要返回列表:

woduplicates = list(set(lseperatedOrblist))

但是,这将会有一个不同的顺序,而不是你原来的列表。


20
值得注意的是,如果您的列表中含有列表或集合,那么这将失败。 - Slater Victoroff
43
请注意,这并不保留元素顺序。 - Michał Górny
2
这并不适用于所有情况,例如在字典列表上就不起作用。 - Aurele Collinet

103

如果你的列表中的项目还没有在新列表中,那么只需创建一个新列表来填充它,否则,只需继续处理原始列表中的下一个项目。

for i in mylist:
  if i not in newlist:
    newlist.append(i)

15
好的,我想我还没忘记我的 Python,只是已经两年了。提醒一下,我相当确定这是一个 O(n^2) 的操作, 因此您可能不希望在大型列表(例如10,000项)上使用它。如果您需要处理大型列表,则最好创建一个哈希表来进行检查(O(1)),从而得到整个操作的O(n)实现,而不是对列表进行检查。但是如果您要处理大型列表,我也不太建议使用 Python。 - Jonathon Vandezande
4
正确的方法是使用set(),请参考cilaris下面的答案。 - Wes Mason
4
这句话的意思是什么?这样做完成了要求的工作,而且不需要额外创建一组数据结构。 - Jonathon Vandezande
6
创建一个集合会打乱顺序。 - alvas
7
这可以维持顺序,并且可以处理不可哈希的列表项,这是一个优点。 - Slater Victoroff
显示剩余4条评论

42

这个做法应该更快,并且会保留原始顺序:

seen = {}
new_list = [seen.setdefault(x, x) for x in my_list if x not in seen]
如果你不关心顺序,你可以这样做:
new_list = list(set(my_list))

3
这是最佳答案,因为它考虑了保序和非保序两种情况。 - Elliot Cameron

32

您可以像这样做:

x = list(set(x))

示例:如果您执行以下操作:

x = [1,2,3,4,5,6,7,8,9,10,2,1,6,31,20]
x = list(set(x))
x

你将会看到以下结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 31]

你只需要考虑一件事情:结果列表将不会与原始列表相同的顺序(在处理过程中会丢失顺序)。


1
聪明且Pythonic :) - mushfiq
4
如果原列表 x 包含子列表,则此方法无法正常工作。 - Calimo

16

保持顺序的现代方法是:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(lseparatedOrbList))

正如Raymond Hettinger在这个回答中所讨论的那样。在Python 3.5及以上版本中,这也是最快的方法 - 有关详细信息,请参见链接的答案。但是,键必须是可散列的(我认为在您的列表中是这种情况)


从Python 3.7开始,有序字典成为一种语言特性,因此上述调用变为:

>>> list(dict.fromkeys(lseparatedOrbList))

表现:

"""Dedup list."""
import sys
import timeit

repeat = 3
numbers = 1000

setup = """"""
def timer(statement, msg='', _setup=None):
    print(msg, min(
        timeit.Timer(statement, setup=_setup or setup).repeat(
            repeat, numbers)))

print(sys.version)
s = """import random; n=%d; li = [random.randint(0, 100) for _ in range(n)]"""
for siz, m in ((150, "\nFew duplicates"), (15000, "\nMany duplicates")):
    print(m)
    setup = s % siz
    timer('s = set(); [i for i in li if i not in s if not s.add(i)]', "s.add(i):")
    timer('list(dict.fromkeys(li))', "dict:")
    timer('list(set(li))', 'Not order preserving: list(set(li)):')

提供:

3.7.6 (tags/v3.7.6:43364a7ae0, Dec 19 2019, 00:42:30) [MSC v.1916 64 bit (AMD64)]

Few duplicates
s.add(i): 0.008242200000040611
dict: 0.0037373999998635554
Not order preserving: list(set(li)): 0.0029409000001123786

Many duplicates
s.add(i): 0.2839437000000089
dict: 0.21970469999996567
Not order preserving: list(set(li)): 0.102068700000018

使用集合set的列表推导式list(set)虽然速度更快,但是无法保留原始列表顺序,而这里需要保留原始顺序。尽管相对于集合添加元素,使用字典dict似乎始终更快一些,但不确定如果进一步改变数字是否会产生不同的结果。


就我所能访问的系统而言,对于一个包含50个随机整数的输入列表,FWIW的执行时间为43微秒,而s = set(); [i for i in input if i not in s if not s.add(i)]则需要7微秒,list(set(input))只需要1.5微秒。 - Masklinn
@Masklinn 我添加了一些时间记录。 - Mr_and_Mrs_D

8
这应该可以为您解决问题:
new_list = list(set(old_list))

set会自动去重,list将其转换回列表。


7
如果原始列表 x 包含子列表,则此方法无效。 - Calimo

8
不,这只是一个笔误,末尾的“list”必须大写。你可以很好地嵌套循环在同一变量上(虽然很少有好的理由)。
然而,代码还存在其他问题。首先,你正在遍历列表,所以i和j将成为项而不是索引。此外,在迭代过程中无法更改集合(当然,它能运行,但那会导致疯狂——例如,你可能会跳过项目)。然后有复杂性问题,你的代码是O(n^2)。要么将列表转换为set并重新转换为列表(简单,但会打乱其余列表项),要么像这样做:
seen = set()
new_x = []
for x in xs:
    if x in seen:
        continue
    seen.add(x)
    new_xs.append(x)

这两种解决方案都需要让项成为可哈希的。如果无法实现,您可能需要保持当前的方法并避免上述问题。


我刚刚点赞了你的回答,但是发现你建议使用列表推导式。如果你像这样使用它:ys = [x for x in xs if x not in ys],那么这个列表推导式将不起作用,因为它基本上会将 xs 列表重写为 ys。这是因为在列表推导式中访问的 ys 是赋值之前的 ys - Tadeck
@Tadeck:该死,你是对的。很好地抓住了问题。 - user395760

5

实际上是因为您缺少了一个大写字母。

有意地取消缩进:

for i in lseparatedOrbList:   # capital 'L'
for j in lseparatedOrblist:   # lowercase 'l'

更高效的方法是将内容插入到一个set中。

如果保持列表顺序很重要(即必须“稳定”),请查看此问题的答案


3
对于不能哈希的列表,可以使用这个方法。因为它不会遍历已经检查过的条目,所以速度更快。
def purge_dublicates(X):
    unique_X = []
    for i, row in enumerate(X):
        if row not in X[i + 1:]:
            unique_X.append(row)
    return unique_X

-3

有一种更快的方法来解决这个问题:

list = [1, 1.0, 1.41, 1.73, 2, 2, 2.0, 2.24, 3, 3, 4, 4, 4, 5, 6, 6, 8, 8, 9, 10]
list2=[]

for value in list:
    try:
        list2.index(value)
    except:
        list2.append(value)
list.clear()
for value in list2:
    list.append(value)
list2.clear()
print(list)
print(list2)

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