删除列表中的重复项

问题描述 投票:803回答:45

几乎我需要编写一个程序来检查列表是否有任何重复项,如果有,它会删除它们并返回一个新列表,其中包含未重复/删除的项目。这就是我所拥有的,但说实话,我不知道该怎么做。

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t
python algorithm list duplicates intersection
45个回答
1402
投票

获得一个独特的项目集合的常用方法是使用set。集合是不同对象的无序集合。要从任何可迭代创建集合,您只需将其传递给内置的set()函数即可。如果您以后再次需要一个真实的列表,您可以类似地将该集合传递给list()函数。

以下示例应涵盖您尝试执行的操作:

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

从示例结果中可以看出,未维护原始订单。如上所述,集合本身是无序集合,因此订单丢失。将集合转换回列表时,会创建任意顺序。

Maintaining order

如果订单对您很重要,那么您将不得不使用不同的机制。一个非常常见的解决方案是依靠OrderedDict在插入过程中保持键的顺序:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Starting with Python 3.7,内置字典也保证维护插入顺序,因此如果您使用的是Python 3.7或更高版本(或CPython 3.6),也可以直接使用它:

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

请注意,这会产生首先创建字典,然后从中创建列表的开销。如果您实际上不需要保留订单,那么最好使用一套。查看this question以获取更多详细信息以及删除重复项时保留订单的其他方法。


最后请注意,set以及OrderedDict / dict解决方案都要求您的物品可以清洗。这通常意味着它们必须是不可变的。如果你必须处理不可清除的项目(例如列表对象),那么你将不得不使用一种缓慢的方法,在这种方法中你基本上必须将每个项目与嵌套循环中的每个其他项目进行比较。


16
投票

简单易用:

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanlist = []
[cleanlist.append(x) for x in myList if x not in cleanlist]

输出:

>>> cleanlist 
[1, 2, 3, 5, 6, 7, 8]

11
投票

我的名单中有一个词典,所以我无法使用上述方法。我收到了错误:

TypeError: unhashable type:

因此,如果您关心订单和/或某些商品是不可取消的。然后你可能会觉得这很有用:

def make_unique(original_list):
    unique_list = []
    [unique_list.append(obj) for obj in original_list if obj not in unique_list]
    return unique_list

有些人可能认为列表理解有副作用,不是一个好的解决方案。这是另一种选择:

def make_unique(original_list):
    unique_list = []
    map(lambda x: unique_list.append(x) if (x not in unique_list) else False, original_list)
    return unique_list

9
投票

到目前为止,我在这里看到的所有顺序保留方法都使用了天真的比较(最好是O(n ^ 2)时间复杂度)或者是限于可输入输入的重量级OrderedDicts / set + list组合。这是一个独立于哈希的O(nlogn)解决方案:

Update添加了key参数,文档和Python 3兼容性。

# from functools import reduce <-- add this import on Python 3

def uniq(iterable, key=lambda x: x):
    """
    Remove duplicates from an iterable. Preserves order. 
    :type iterable: Iterable[Ord => A]
    :param iterable: an iterable of objects of any orderable type
    :type key: Callable[A] -> (Ord => B)
    :param key: optional argument; by default an item (A) is discarded 
    if another item (B), such that A == B, has already been encountered and taken. 
    If you provide a key, this condition changes to key(A) == key(B); the callable 
    must return orderable objects.
    """
    # Enumerate the list to restore order lately; reduce the sorted list; restore order
    def append_unique(acc, item):
        return acc if key(acc[-1][1]) == key(item[1]) else acc.append(item) or acc 
    srt_enum = sorted(enumerate(iterable), key=lambda item: key(item[1]))
    return [item[1] for item in sorted(reduce(append_unique, srt_enum, [srt_enum[0]]))] 

7
投票

尝试使用套装:

import sets
t = sets.Set(['a', 'b', 'c', 'd'])
t1 = sets.Set(['a', 'b', 'c'])

print t | t1
print t - t1

7
投票

你也可以这样做:

>>> t = [1, 2, 3, 3, 2, 4, 5, 6]
>>> s = [x for i, x in enumerate(t) if i == t.index(x)]
>>> s
[1, 2, 3, 4, 5, 6]

上述原因是index方法只返回元素的第一个索引。重复元素具有更高的索引。请参阅here

list.index(x [,start [,end]]) 在值为x的第一个项的列表中返回从零开始的索引。如果没有这样的项,则引发ValueError。


6
投票

通过订购保留减少变量:

假设我们有列表:

l = [5, 6, 6, 1, 1, 2, 2, 3, 4]

减少变量(效率低):

>>> reduce(lambda r, v: v in r and r or r + [v], l, [])
[5, 6, 1, 2, 3, 4]

5倍速但更复杂

>>> reduce(lambda r, v: v in r[1] and r or (r[0].append(v) or r[1].add(v)) or r, l, ([], set()))[0]
[5, 6, 1, 2, 3, 4]

说明:

default = (list(), set())
# user list to keep order
# use set to make lookup faster

def reducer(result, item):
    if item not in result[1]:
        result[0].append(item)
        result[1].add(item)
    return result

reduce(reducer, l, default)[0]

6
投票

从列表中删除重复项的最佳方法是使用python中提供的set()函数,再次将该集转换为列表

In [2]: some_list = ['a','a','v','v','v','c','c','d']
In [3]: list(set(some_list))
Out[3]: ['a', 'c', 'd', 'v']

6
投票

您可以使用以下功能:

def rem_dupes(dup_list): 
    yooneeks = [] 
    for elem in dup_list: 
        if elem not in yooneeks: 
            yooneeks.append(elem) 
    return yooneeks

例:

my_list = ['this','is','a','list','with','dupicates','in', 'the', 'list']

用法:

rem_dupes(my_list)

['this','is','a','list','with','duplicates','in','the']


6
投票

如果您想保留订单,而不是使用任何外部模块,这是一种简单的方法:

>>> t = [1, 9, 2, 3, 4, 5, 3, 6, 7, 5, 8, 9]
>>> list(dict.fromkeys(t))
[1, 9, 2, 3, 4, 5, 6, 7, 8]

注意:此方法保留了外观的顺序,因此,如上所示,九个将在一个之后,因为它是第一次出现。然而,这与您所做的结果相同

from collections import OrderedDict
ulist=list(OrderedDict.fromkeys(l))

但它更短,运行速度更快。

这是有效的,因为每次fromkeys函数尝试创建一个新键时,如果该值已经存在,它将只是覆盖它。然而,这根本不会影响字典,因为fromkeys创建了一个字典,其中所有键都具有值None,因此它有效地以这种方式消除了所有重复。


5
投票

还有许多其他答案提出了不同的方法来做到这一点,但它们都是批处理操作,其中一些抛弃了原始订单。这可能是好的,具体取决于你需要什么,但如果你想按照每个值的第一个实例的顺序迭代值,并且你想要一次性删除重复项,你可以使用这个发电机:

def uniqify(iterable):
    seen = set()
    for item in iterable:
        if item not in seen:
            seen.add(item)
            yield item

这将返回一个生成器/迭代器,因此您可以在任何可以使用迭代器的地方使用它。

for unique_item in uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]):
    print(unique_item, end=' ')

print()

输出:

1 2 3 4 5 6 7 8

如果你想要一个list,你可以这样做:

unique_list = list(uniqify([1, 2, 3, 4, 3, 2, 4, 5, 6, 7, 6, 8, 8]))

print(unique_list)

输出:

[1, 2, 3, 4, 5, 6, 7, 8]

379
投票

在Python 2.7中,从迭代中删除重复项同时保持原始顺序的新方法是:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

在Python 3.5中,OrderedDict有一个C实现。我的时间表明,现在这是Python 3.5的各种方法中最快和最短的。

在Python 3.6中,常规字典变得有序且紧凑。 (此功能适用于CPython和PyPy,但在其他实现中可能不存在)。这为我们提供了一种新的最快的扣除方式,同时保留了订单:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

在Python 3.7中,保证常规字典在所有实现中都有序。因此,最短和最快的解决方案是:

>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']

5
投票

不使用set

data=[1, 2, 3, 1, 2, 5, 6, 7, 8]
uni_data=[]
for dat in data:
    if dat not in uni_data:
        uni_data.append(dat)

print(uni_data) 

4
投票

这个人关心订单没有太多麻烦(OrderdDict和其他人)。可能不是最恐怖的方式,也不是最短的方式,但是诀窍是:

def remove_duplicates(list):
    ''' Removes duplicate items from a list '''
    singles_list = []
    for element in list:
        if element not in singles_list:
            singles_list.append(element)
    return singles_list

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]


4
投票

使用集合:

a = [0,1,2,3,4,3,3,4]
a = list(set(a))
print a

使用独特:

import numpy as np
a = [0,1,2,3,4,3,3,4]
a = np.unique(a).tolist()
print a

4
投票

还有一种更好的方法,

import pandas as pd

myList = [1, 2, 3, 1, 2, 5, 6, 7, 8]
cleanList = pd.Series(myList).drop_duplicates().tolist()
print(cleanList)

#> [1, 2, 3, 5, 6, 7, 8]

并且订单仍然保留。


3
投票

与回复中列出的其他解决方案相比,这是最快的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

3
投票

Python 3中非常简单的方法:

>>> n = [1, 2, 3, 4, 1, 1]
>>> n
[1, 2, 3, 4, 1, 1]
>>> m = sorted(list(set(n)))
>>> m
[1, 2, 3, 4]

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
投票

如果你想删除重复项(就地编辑而不是返回新列表)而不使用内置集,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]

2
投票

我认为转换为set是删除重复的最简单方法:

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

180
投票

这是一个单线:list(set(source_list))将成功。

set是不可能有重复的东西。

更新:订单保留方法有两行:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

这里我们使用OrderedDict记住键的插入顺序这一事实,并且在更新特定键的值时不会更改它。我们插入True作为值,但我们可以插入任何内容,只是不使用值。 (set的工作方式很像dict,也被忽略了。)


2
投票

不幸。这里的大多数答案要么不保留订单,要么太长。这是一个简单的订单保留答案。

s = [1,2,3,4,5,2,5,6,7,1,3,9,3,5]
x=[]

[x.append(i) for i in s if i not in x]
print(x)

这将为您提供x,删除重复但保留订单。


85
投票
>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> s = []
>>> for i in t:
       if i not in s:
          s.append(i)
>>> s
[1, 2, 3, 5, 6, 7, 8]

78
投票

如果您不关心订单,请执行以下操作:

def remove_duplicates(l):
    return list(set(l))

保证set没有重复。


37
投票

创建一个新列表,保留L中重复项的第一个元素的顺序

newlist=[ii for n,ii in enumerate(L) if ii not in L[:n]]

例如if L=[1, 2, 2, 3, 4, 2, 4, 3, 5]然后newlist将是[1,2,3,4,5]

这会在添加之前检查先前未在列表中出现的每个新元素。它也不需要进口。


22
投票

一位同事已将接受的答案作为其代码的一部分发送给我,以便我今天进行代码审查。虽然我当然钦佩有问题的答案的优雅,但我对表现并不满意。我试过这个解决方案(我用set来减少查找时间)

def ordered_set(in_list):
    out_list = []
    added = set()
    for val in in_list:
        if not val in added:
            out_list.append(val)
            added.add(val)
    return out_list

为了比较效率,我使用了100个整数的随机样本--62个是唯一的

from random import randint
x = [randint(0,100) for _ in xrange(100)]

In [131]: len(set(x))
Out[131]: 62

以下是测量结果

In [129]: %timeit list(OrderedDict.fromkeys(x))
10000 loops, best of 3: 86.4 us per loop

In [130]: %timeit ordered_set(x)
100000 loops, best of 3: 15.1 us per loop

那么,如果从解决方案中删除了set,​​会发生什么?

def ordered_set(inlist):
    out_list = []
    for val in inlist:
        if not val in out_list:
            out_list.append(val)
    return out_list

结果没有OrderedDict那么糟糕,但仍然是原始解决方案的3倍多

In [136]: %timeit ordered_set(x)
10000 loops, best of 3: 52.6 us per loop

20
投票

另一种做法:

>>> seq = [1,2,3,'a', 'a', 1,2]
>> dict.fromkeys(seq).keys()
['a', 1, 2, 3]

20
投票

还有使用Pandas和Numpy的解决方案。它们都返回numpy数组,因此如果你想要一个列表,你必须使用.tolist()函数。

t=['a','a','b','b','b','c','c','c']
t2= ['c','c','b','b','b','a','a','a']

熊猫解决方案

使用Pandas函数unique()

import pandas as pd
pd.unique(t).tolist()
>>>['a','b','c']
pd.unique(t2).tolist()
>>>['c','b','a']

Numpy解决方案

使用numpy函数unique()

import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']

请注意,numpy.unique()也会对值进行排序。因此列表t2被返回排序。如果您希望保留订单,请使用this answer

_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']

与其他解决方案相比,解决方案并不那么优雅,但与pandas.unique()相比,numpy.unique()还允许您检查嵌套数组是否沿一个选定的轴是唯一的。

© www.soinside.com 2019 - 2024. All rights reserved.