如何检查列表是否有任何重复项,并返回一个没有重复项的新列表?
set
。集合是无序的,包含不同对象的集合。要从任何可迭代对象创建一个集合,只需将其传递给内置的 set()
函数即可。如果您稍后需要一个真正的列表,可以类似地将集合传递给 list()
函数。>>> t = [1, 2, 3, 1, 2, 3, 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]
如果顺序对您很重要,则必须使用不同的机制。一个非常常见的解决方案是依靠 OrderedDict
在插入期间保持键的顺序:
>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]
从Python 3.7开始, 内置字典保证维护插入顺序,因此如果您使用的是Python 3.7或更高版本(或者CPython 3.6),您也可以直接使用它:
>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]
set
还是OrderedDict
/dict
解决方案都需要你的项目是可哈希的。这通常意味着它们必须是不可变的。如果你必须处理不可哈希的项目(例如列表对象),那么你将不得不使用一种缓慢的方法,在其中你将基本上必须在嵌套循环中将每个项目与每个其他项目进行比较。[dict(d) for d in set([frozenset(i.items()) for i in t])]
- Fredrik Erlandssondict.fromkeys()
可以在线性时间内创建一个字典,而 list()
也可以在线性时间内从中创建一个列表。 - poke在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']
一行代码解决问题:list(set(source_list))
。
set
是不可能有重复项的。
更新:保留顺序的做法是两行:
from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()
我们在这里利用了 OrderedDict
记住键的插入顺序的特性,并且在特定键的值被更新时不会改变它。我们将 True
作为值插入,但我们也可以插入任何其他值。 (set
的工作方式与忽略值的 dict
很像。)
>>> 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]
如果您不关心顺序,可以这样做:
def remove_duplicates(l):
return list(set(l))
set
保证不会有重复元素。
为了创建一个新的列表,保留L
中重复元素的第一个出现的顺序:
newlist = [ii for n,ii in enumerate(L) if ii not in L[:n]]
例如:如果 L = [1, 2, 2, 3, 4, 2, 4, 3, 5]
,那么 newlist
将会是 [1, 2, 3, 4, 5]
。
这个操作检查每个新元素在添加之前是否已经出现在列表中。
而且它不需要导入。set
和OrderedDict
可能会有更低的摊销时间复杂度。 - blubberdiblubin
运算符并不像人们期望的那样工作(至少我是这么期望的)。 - Keta还有使用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函数unique()
。
import numpy as np
np.unique(t).tolist()
>>>['a','b','c']
np.unique(t2).tolist()
>>>['a','b','c']
_, idx = np.unique(t2, return_index=True)
t2[np.sort(idx)].tolist()
>>>['c','b','a']
from collections import OrderedDict, Counter
class Container:
def __init__(self, obj):
self.obj = obj
def __eq__(self, obj):
return self.obj == obj
def __hash__(self):
try:
return hash(self.obj)
except:
return id(self.obj)
class OrderedCounter(Counter, OrderedDict):
'Counter that remembers the order elements are first encountered'
def __repr__(self):
return '%s(%r)' % (self.__class__.__name__, OrderedDict(self))
def __reduce__(self):
return self.__class__, (OrderedDict(self),)
def remd(sequence):
cnt = Counter()
for x in sequence:
cnt[Container(x)] += 1
return [item.obj for item in cnt]
def oremd(sequence):
cnt = OrderedCounter()
for x in sequence:
cnt[Container(x)] += 1
return [item.obj for item in cnt]
remd
是无序排序,而oremd
是有序排序。你可以清楚地看出哪一个更快,但我还是会解释一下。非有序排序略快,因为它不存储项目的顺序。
现在,我也想展示每个答案的速度比较。所以,我现在就这样做。
对于去重,我从几个答案中收集了10个函数。我计算了每个函数的速度,并使用matplotlib.pyplot将其放入图表中。
我将此分为三轮绘图。可哈希对象是任何可以哈希的对象,不可哈希对象是任何不能哈希的对象。有序序列是保留顺序的序列,无序序列不保留顺序。现在,这里还有一些术语:
无序可哈希是用于删除重复项的任何方法,它不一定要保持顺序。它不必适用于不可哈希对象,但它可以。
有序可哈希是保持列表中项目顺序的任何方法,但它不必适用于不可哈希对象,但它可以。
有序不可哈希是保持列表中项目顺序并适用于不可哈希对象的任何方法。
在y轴上是所需的秒数。
在x轴上是应用该函数的数字。
我使用以下推导式为无序可散列对象和有序可散列对象生成序列:[list(range(x)) + list(range(x)) for x in range(0, 1000, 10)]
对于有序的不可散列对象:[[list(range(y)) + list(range(y)) for y in range(x)] for x in range(0, 1000, 10)]
请注意,范围中有一个step
,因为没有它,这将需要10倍的时间。另外,因为在我个人看来,我认为这可能会看起来更容易阅读。
还要注意图例上的键是我尝试猜测函数实现中最关键的部分。至于哪个函数做得最好或最差?图表说明了一切。
有了这个解决,这里是图表。
今天我的一个同事在代码审查中将他的已接受答案发送给了我。 虽然我确实欣赏所提出答案的优雅,但是我对其性能并不满意。 我已经尝试过这个解决方案(我使用 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
好的,如果从解决方案中删除了集合会发生什么?
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
def unique(iterable):
; seen = set()
; seen_add = seen.add
; return [item for item in iterable if not item in seen and not seen_add(item)]
- DrD
[1, 2, 3, 4, 5, 2, 4]
->[1, 3, 5]
,因为2和4是重复的。 - 9769953[1,2,3,1]→[1,2,3]
)是否有意义? 接受的答案暗示了可能实现第二个子问题的方法(即[1,2,3,1]→[2,3]
)。 目前,问题和最佳答案在某种程度上不完全同步。 - Mateen Ulhaq