在列表中去除重复项

1448

如何检查列表是否有任何重复项,并返回一个没有重复项的新列表?


1
如何使用多进程在一个非常大的列表中删除重复项? - Darkonaut
1
有趣的是,这里的所有顶级答案都没有回答实际问题:创建一个仅包含原始列表中未重复项的新列表。我将其解读为 [1, 2, 3, 4, 5, 2, 4] -> [1, 3, 5],因为2和4是重复的。 - 9769953
根据您的说法,使用Rev 11并仅保留由顶部答案回答的第一个子问题(即[1,2,3,1]→[1,2,3])是否有意义? 接受的答案暗示了可能实现第二个子问题的方法(即[1,2,3,1]→[2,3])。 目前,问题和最佳答案在某种程度上不完全同步。 - Mateen Ulhaq
@MateenUlhaq 我更喜欢保留原始问题。此外,第11版更改了问题以更好地适应答案,但不一定适合原始问题。我想这取决于您希望SO成为多少论坛/邮件列表风格,或者与技巧和技巧网站(具有非常纯净的问题和答案)有多接近。我认为两者都无法实现。 - 9769953
换句话说,这将使问题成为 从另一个列表中删除所有出现的元素 的重复,该问题从一开始就提得更好。但似乎几乎每个人都看到了不同的问题。 - Karl Knechtel
显示剩余2条评论
58个回答

4

下面的代码是用于在列表中去除重复项的简单方法

def remove_duplicates(x):
    a = []
    for i in x:
        if i not in a:
            a.append(i)
    return a

print remove_duplicates([1,2,2,3,3,4])

它返回 [1,2,3,4]


2
如果您不关心顺序,那么这将需要更长的时间。使用list(set(..))(超过100万次)将比此解决方案快约10秒钟 - 而此方法需要大约12秒钟,list(set(..))仅需要约2秒钟! - dylnmc
@dylnmc 这也是一个重复的问题,与一个早期的答案相同。 - Eli Korvigo

4
这是与回复中列出的其他解决方案相比最快的pythonic解决方案。
利用短路求值的实现细节,可以使用列表推导式,这是足够快的。visited.add(item)始终返回None作为结果,这被评估为False,因此or的右侧总是这样一个表达式的结果。
自己计时吧。
def deduplicate(sequence):
    visited = set()
    adder = visited.add  # get rid of qualification overhead
    out = [adder(item) or item for item in sequence if item not in visited]
    return out

2
def remove_duplicates(A):
   [A.pop(idx) for idx,elem in enumerate(A) if A.count(elem)!=1]
   return A

一个用于去除重复项的列表推导式。

喜欢它的简洁性,对于小型列表这将很有效。我倾向于不使用'count'作为索引,而是使用'idx',例如:[A.pop(idx) for idx,elem in enumerate(A) if A.count(elem)!=1] - Bert Bril

2

如果您想删除重复项(原地编辑而不返回新列表),而不使用内置的set、dict.keys、uniqify和counter,请勾选此项。

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> for i in t:
...     if i in t[t.index(i)+1:]:
...         t.remove(i)
... 
>>> t
[3, 1, 2, 5, 6, 7, 8]

使用enumerate()函数可以更快地获取索引:for i, value in enumerate(t): if value in t[i + 1:]: t.remove(value) - Martijn Pieters

2

这里有一个例子,返回不重复的列表并保留顺序。不需要任何外部导入。

def GetListWithoutRepetitions(loInput):
    # return list, consisting of elements of list/tuple loInput, without repetitions.
    # Example: GetListWithoutRepetitions([None,None,1,1,2,2,3,3,3])
    # Returns: [None, 1, 2, 3]

    if loInput==[]:
        return []

    loOutput = []

    if loInput[0] is None:
        oGroupElement=1
    else: # loInput[0]<>None
        oGroupElement=None

    for oElement in loInput:
        if oElement<>oGroupElement:
            loOutput.append(oElement)
            oGroupElement = oElement
    return loOutput

2

我认为将其转换为集合是去除重复项的最简单方法:

list1 = [1,2,1]
list1 = list(set(list1))
print list1

2

我没有看到关于不可哈希值的答案,这里是一个一行代码、n log n时间复杂度、仅使用标准库的答案:

list(map(operator.itemgetter(0), itertools.groupby(sorted(items))))

或者作为生成器函数:

def unique(items: Iterable[T]) -> Iterable[T]:
    """For unhashable items (can't use set to unique) with a partial order"""
    yield from map(operator.itemgetter(0), itertools.groupby(sorted(items)))

1
这里有很多答案使用了 set(..)(如果元素是可哈希的,则速度较快),或者列表(缺点是它会导致 O(n2) 算法)。
我提出的函数是一种混合型函数:我们对于可哈希的物品使用 set(..),对于不可哈希的物品使用 list(..)。此外,它被实现为一个生成器,因此我们可以限制项目数量,或进行其他过滤。
最后,我们还可以使用 key 参数来指定元素应该以何种方式唯一。例如,如果我们想要过滤字符串列表,使得输出中的每个字符串长度都不同,就可以使用它。
def uniq(iterable, key=lambda x: x):
    seens = set()
    seenl = []
    for item in iterable:
        k = key(item)
        try:
            seen = k in seens
        except TypeError:
            seen = k in seenl
        if not seen:
            yield item
            try:
                seens.add(k)
            except TypeError:
                seenl.append(k)

我们现在可以像这样使用它:

我们现在比如可以这样使用它:

>>> list(uniq(["apple", "pear", "banana", "lemon"], len))
['apple', 'pear', 'banana']
>>> list(uniq(["apple", "pear", "lemon", "banana"], len))
['apple', 'pear', 'banana']
>>> list(uniq(["apple", "pear", {}, "lemon", [], "banana"], len))
['apple', 'pear', {}, 'banana']
>>> list(uniq(["apple", "pear", {}, "lemon", [], "banana"]))
['apple', 'pear', {}, 'lemon', [], 'banana']
>>> list(uniq(["apple", "pear", {}, "lemon", {}, "banana"]))
['apple', 'pear', {}, 'lemon', 'banana']

因此,它是一个可以在任何可迭代对象上工作并过滤出唯一值的筛选器,无论这些值是否可哈希。
它有一个假设:如果一个对象是可哈希的,而另一个对象不是,那么这两个对象永远不相等。严格来说,这种情况可能会发生,但这很少见。

注意:有一些内置函数会打破最后一段所述的假设;frozenset是可哈希的,而set则不是,如果它们具有相同的值,则它们是相等的,但在此代码中,您将把它们视为不相等。 - ShadowRanger
@ShadowRanger:是的,我同意这一点,就像所说的那样,它并不能解决所有问题。然而,通过使用set(..),这根本不起作用,而通过使用list,这将导致线性查找时间。因此,它被认为是一个“更好”的集合,但也有一些缺陷。 - Willem Van Onsem
此外,set(..) 在极少数情况下还会返回不相等的对象。例如 math.nan 不等于 math.nan,但由于字典首先检查引用相等性,因此字典将返回它。 - Willem Van Onsem
不要使用 key=lambda x: x,你可以使用 key=None 并且输入 k = key(item) if key else item。这样会稍微快一点,并且得到相同的结果。 - Timothy C. Quinn

1
另一个解决方案可能是这样的。将列表转换为字典,以项目为键,索引为值,然后打印字典键。
>>> lst = [1, 3, 4, 2, 1, 21, 1, 32, 21, 1, 6, 5, 7, 8, 2]
>>>
>>> dict_enum = {item:index for index, item in enumerate(lst)}
>>> print dict_enum.keys()
[32, 1, 2, 3, 4, 5, 6, 7, 8, 21]

为什么要计算/存储索引,如果你从未使用它呢?这看起来像是一种旨在保留顺序的解决方案(通过存储每个值的最后一个索引),但却忘记了这样做。list(set(lst))可以实现相同的逻辑结果。 - ShadowRanger
你可以直接使用 list(dict.fromkeys(lst)) - Brayoni

1
你可以简单地使用集合来完成这个操作。 步骤1:获取列表中不同的元素
步骤2:获取列表中共同的元素
步骤3:将它们组合起来。
In [1]: a = ["apples", "bananas", "cucumbers"]

In [2]: b = ["pears", "apples", "watermelons"]

In [3]: set(a).symmetric_difference(b).union(set(a).intersection(b))
Out[3]: {'apples', 'bananas', 'cucumbers', 'pears', 'watermelons'}

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