例如:
>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]
假设列表元素是可散列的。
澄清:结果应保留列表中的第一个重复项。例如,[1, 2, 3, 2, 3, 1] 变为 [1, 2, 3]。
def unique(items):
found = set()
keep = []
for item in items:
if item not in found:
found.add(item)
keep.append(item)
return keep
print unique([1, 1, 2, 'a', 'a', 3])
使用:
lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18, 13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1, 5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9, 9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
并使用 timeit 模块:
$ python -m timeit -s 'import uniquetest' 'uniquetest.etchasketch(uniquetest.lst)'
等等其他各种功能(我以他们的海报命名),我有以下结果(在我的第一代英特尔 MacBook Pro 上):
Allen: 14.6 µs per loop [1]
Terhorst: 26.6 µs per loop
Tarle: 44.7 µs per loop
ctcherry: 44.8 µs per loop
Etchasketch 1 (short): 64.6 µs per loop
Schinckel: 65.0 µs per loop
Etchasketch 2: 71.6 µs per loop
Little: 89.4 µs per loop
Tyler: 179.0 µs per loop
[1] 请注意,Allen 修改了列表 - 我相信这已经扭曲了时间,因为该timeit
模块运行代码 100000 次,其中 99999 次与无重复列表一起运行。
总结:使用集合的直接实现胜过令人困惑的单行代码:-)
更新:在 Python3.7+ 上:
>>> list(dict.fromkeys('abracadabra'))
['a', 'b', 'r', 'c', 'd']
旧答案:
这是迄今为止最快的解决方案(对于以下输入):
def del_dups(seq):
seen = {}
pos = 0
for item in seq:
if item not in seen:
seen[item] = True
seq[pos] = item
pos += 1
del seq[pos:]
lst = [8, 8, 9, 9, 7, 15, 15, 2, 20, 13, 2, 24, 6, 11, 7, 12, 4, 10, 18,
13, 23, 11, 3, 11, 12, 10, 4, 5, 4, 22, 6, 3, 19, 14, 21, 11, 1,
5, 14, 8, 0, 1, 16, 5, 10, 13, 17, 1, 16, 17, 12, 6, 10, 0, 3, 9,
9, 3, 7, 7, 6, 6, 7, 5, 14, 18, 12, 19, 2, 8, 9, 0, 8, 4, 5]
del_dups(lst)
print(lst)
# -> [8, 9, 7, 15, 2, 20, 13, 24, 6, 11, 12, 4, 10, 18, 23, 3, 5, 22, 19, 14,
# 21, 1, 0, 16, 17]
字典查找比 Python 3 中的集合稍快。
最快的取决于您的列表中有多少百分比是重复的。如果它几乎都是重复的,只有很少的独特项目,那么创建一个新列表可能会更快。如果它主要是独特的项目,从原始列表(或副本)中删除它们会更快。
这是一个用于修改列表的方法:
def unique(items):
seen = set()
for i in xrange(len(items)-1, -1, -1):
it = items[i]
if it in seen:
del items[i]
else:
seen.add(it)
在索引上向后迭代可确保删除项目不会影响迭代。
这是我发现的最快的就地方法(假设有很大一部分重复):
def unique(l):
s = set(); n = 0
for x in l:
if x not in s: s.add(x); l[n] = x; n += 1
del l[n:]
这比它所基于的 Allen 的实现快 10%(与 timeit.repeat 计时,由 psyco 编译的 JIT)。它保留任何重复的第一个实例。
repton-infinity:如果你能确认我的时间安排,我会很感兴趣。
基于生成器的强制性变体:
def unique(seq):
seen = set()
for x in seq:
if x not in seen:
seen.add(x)
yield x
这可能是最简单的方法:
list(OrderedDict.fromkeys(iterable))
从 Python 3.5 开始,OrderedDict 现在是用 C 实现的,所以这是现在最短、最干净、最快的。
取自http://www.peterbe.com/plog/uniqifiers-benchmark
def f5(seq, idfun=None):
# order preserving
if idfun is None:
def idfun(x): return x
seen = {}
result = []
for item in seq:
marker = idfun(item)
# in old Python versions:
# if seen.has_key(marker)
# but in new ones:
if marker in seen: continue
seen[marker] = 1
result.append(item)
return result
单线:
new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, [])
为此的就地单线:
>>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3]
这是最快的一个,比较了这个冗长讨论中的所有内容和这里给出的其他答案,参考这个基准。它比讨论中最快的函数快了 25% f8
,. 感谢大卫柯比的想法。
def uniquify(seq):
seen = set()
seen_add = seen.add
return [x for x in seq if x not in seen and not seen_add(x)]
一些时间比较:
$ python uniqifiers_benchmark.py
* f8_original 3.76
* uniquify 3.0
* terhorst 5.44
* terhorst_localref 4.08
* del_dups 4.76
你实际上可以在 Python 中做一些非常酷的事情来解决这个问题。您可以创建一个列表推导式,该列表推导式将在构建时引用自身。如下:
# remove duplicates...
def unique(my_list):
return [x for x in my_list if x not in locals()['_[1]'].__self__]
编辑:我删除了“自我”,它适用于 Mac OS X、Python 2.5.1。
_[1] 是 Python 对新列表的“秘密”引用。当然,上述内容有点混乱,但您可以根据需要调整它以满足您的需求。例如,您实际上可以编写一个返回对推导的引用的函数;它看起来更像:
return [x for x in my_list if x not in this_list()]
重复项是否必须首先出现在列表中?就查找元素而言,没有任何开销,但在添加元素时会有更多开销(尽管开销应该是 O(1) )。
>>> x = []
>>> y = set()
>>> def add_to_x(val):
... if val not in y:
... x.append(val)
... y.add(val)
... print x
... print y
...
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>> add_to_x(1)
[1]
set([1])
>>>
删除重复项并保留顺序:
这是一个快速的 2-liner,它利用了列表理解和字典的内置功能。
x = [1, 1, 2, 'a', 'a', 3]
tmpUniq = {} # temp variable used below
results = [tmpUniq.setdefault(i,i) for i in x if i not in tmpUniq]
print results
[1, 2, 'a', 3]
dict.setdefaults() 函数返回值并将其直接添加到列表理解中的临时字典中。使用内置函数和 dict 的哈希值将有助于最大限度地提高流程效率。
这里有一些很棒的、有效的解决方案。但是,对于那些不关心绝对最有效的O(n)
解决方案的人,我会选择简单的单线O(n^2*log(n))
解决方案:
def unique(xs):
return sorted(set(xs), key=lambda x: xs.index(x))
或更有效的两线O(n*log(n))
解决方案:
def unique(xs):
positions = dict((e,pos) for pos,e in reversed(list(enumerate(xs))))
return sorted(set(xs), key=lambda x: positions[x])
python中的has_key是O(1)。从哈希中插入和检索也是 O(1)。循环遍历 n 个项目两次,所以 O(n)。
def unique(list):
s = {}
output = []
for x in list:
count = 1
if(s.has_key(x)):
count = s[x] + 1
s[x] = count
for x in list:
count = s[x]
if(count > 0):
s[x] = 0
output.append(x)
return output
O(n) 如果 dict 是哈希,O(nlogn) 如果 dict 是树,并且简单,固定。感谢马修的建议。抱歉,我不知道基础类型。
def unique(x):
output = []
y = {}
for item in x:
y[item] = ""
for item in x:
if item in y:
output.append(item)
return output
以下是itertools文档中的两个配方:
def unique_everseen(iterable, key=None):
"List unique elements, preserving order. Remember all elements ever seen."
# unique_everseen('AAAABBBCCDAABBB') --> A B C D
# unique_everseen('ABBCcAD', str.lower) --> A B C D
seen = set()
seen_add = seen.add
if key is None:
for element in ifilterfalse(seen.__contains__, iterable):
seen_add(element)
yield element
else:
for element in iterable:
k = key(element)
if k not in seen:
seen_add(k)
yield element
def unique_justseen(iterable, key=None):
"List unique elements, preserving order. Remember only the element just seen."
# unique_justseen('AAAABBBCCDAABBB') --> A B C D A B
# unique_justseen('ABBCcAD', str.lower) --> A B C A D
return imap(next, imap(itemgetter(1), groupby(iterable, key)))
我没有使用 python 的经验,但是一种算法是对列表进行排序,然后删除重复项(通过与列表中的先前项目进行比较),最后通过与旧列表进行比较来找到新列表中的位置。
更长的答案:http ://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560
>>> def unique(list):
... y = []
... for x in list:
... if x not in y:
... y.append(x)
... return y
如果您在 Terhost 的回答中从对 set() 的调用中取出空列表,您会获得一点速度提升。
将:found = set([]) 更改
为:found = set()
但是,您根本不需要该集合。
def unique(items):
keep = []
for item in items:
if item not in keep:
keep.append(item)
return keep
使用 timeit 我得到了这些结果:
有 set([]) -- 4.97210427363
有 set() -- 4.65712377445
没有设置 -- 3.44865284975
x = [] # Your list of items that includes Duplicates
# Assuming that your list contains items of only immutable data types
dict_x = {}
dict_x = {item : item for i, item in enumerate(x) if item not in dict_x.keys()}
# Average t.c. = O(n)* O(1) ; furthermore the dict comphrehension and generator like behaviour of enumerate adds a certain efficiency and pythonic feel to it.
x = dict_x.keys() # if you want your output in list format
>>> x=[1,1,2,'a','a',3]
>>> y = [ _x for _x in x if not _x in locals()['_[1]'] ]
>>> y
[1, 2, 'a', 3]
“locals()['_[1]']”是正在创建的列表的“秘密名称”。
I don't know if this one is fast or not, but at least it is simple.
Simply, convert it first to a set and then again to a list
def unique(container):
return list(set(container))
我没有做任何测试,但一种可能的算法可能是创建第二个列表,并遍历第一个列表。如果项目不在第二个列表中,请将其添加到第二个列表中。
x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
if each not in y:
y.append(each)
一通。
a = [1,1,'a','b','c','c']
new_list = []
prev = None
while 1:
try:
i = a.pop(0)
if i != prev:
new_list.append(i)
prev = i
except IndexError:
break
a=[1,2,3,4,5,7,7,8,8,9,9,3,45]
def unique(l):
ids={}
for item in l:
if not ids.has_key(item):
ids[item]=item
return ids.keys()
print a
print unique(a)
插入元素将花费 theta(n) 检索元素是否退出将花费恒定时间测试所有项目也将花费 theta(n),因此我们可以看到此解决方案将采用 theta(n)。请记住,python 中的字典是由哈希表实现的。