45

例如:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]

假设列表元素是可散列的。

澄清:结果应保留列表中的第一个重复项。例如,[1, 2, 3, 2, 3, 1] 变为 [1, 2, 3]。

4

27 回答 27

33
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])
于 2008-09-18T01:41:18.170 回答
19

使用:

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 次与无重复列表一起运行。


总结:使用集合的直接实现胜过令人困惑的单行代码:-)

于 2008-09-18T05:14:19.750 回答
18

更新在 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 中的集合稍快。

于 2008-11-12T00:04:22.647 回答
15

最快的取决于您的列表中有多少百分比是重复的。如果它几乎都是重复的,只有很少的独特项目,那么创建一个新列表可能会更快。如果它主要是独特的项目,从原始列表(或副本)中删除它们会更快。

这是一个用于修改列表的方法:

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)

在索引上向后迭代可确保删除项目不会影响迭代。

于 2008-09-18T01:33:44.333 回答
10

这是我发现的最快的就地方法(假设有很大一部分重复):

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:如果你能确认我的时间安排,我会很感兴趣。

于 2008-09-18T10:17:44.777 回答
7

基于生成器的强制性变体:

def unique(seq):
  seen = set()
  for x in seq:
    if x not in seen:
      seen.add(x)
      yield x
于 2008-09-27T15:54:03.440 回答
7

这可能是最简单的方法:

list(OrderedDict.fromkeys(iterable))

从 Python 3.5 开始,OrderedDict 现在是用 C 实现的,所以这是现在最短、最干净、最快的。

于 2011-10-21T01:10:05.853 回答
5

取自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
于 2008-09-18T01:30:50.410 回答
5

单线:

new_list = reduce(lambda x,y: x+[y][:1-int(y in x)], my_list, [])
于 2008-09-18T01:59:05.833 回答
4

为此的就地单线:

>>> x = [1, 1, 2, 'a', 'a', 3]
>>> [ item for pos,item in enumerate(x) if x.index(item)==pos ]
[1, 2, 'a', 3]
于 2010-04-09T13:11:48.107 回答
4

这是最快的一个,比较了这个冗长讨论中的所有内容和这里给出的其他答案,参考这个基准。它比讨论中最快的函数快了 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
于 2013-12-26T15:38:50.320 回答
3

你实际上可以在 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()]

于 2008-09-18T01:43:03.703 回答
2

重复项是否必须首先出现在列表中?就查找元素而言,没有任何开销,但在添加元素时会有更多开销(尽管开销应该是 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])
>>> 
于 2008-09-18T02:06:40.700 回答
2

删除重复项并保留顺序:

这是一个快速的 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 的哈希值将有助于最大限度地提高流程效率。

于 2010-12-29T17:11:51.973 回答
1

这里有一些很棒的、有效的解决方案。但是,对于那些不关心绝对最有效的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])
于 2008-09-18T13:23:05.673 回答
1

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
于 2008-09-18T04:07:21.347 回答
1

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
于 2008-09-18T01:35:33.130 回答
1

以下是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)))
于 2011-10-21T01:07:11.860 回答
0

我没有使用 python 的经验,但是一种算法是对列表进行排序,然后删除重复项(通过与列表中的先前项目进行比较),最后通过与旧列表进行比较来找到新列表中的位置。

更长的答案:http ://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52560

于 2008-09-18T01:29:06.903 回答
0
>>> def unique(list):
...   y = []
...   for x in list:
...     if x not in y:
...       y.append(x)
...   return y
于 2008-09-18T01:32:47.457 回答
0

如果您在 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

于 2008-09-19T09:42:20.657 回答
0
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 
于 2014-09-21T11:26:53.927 回答
-1
>>> 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]']”是正在创建的列表的“秘密名称”。

于 2008-09-18T01:54:40.027 回答
-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))
于 2008-09-18T08:51:32.660 回答
-2

我没有做任何测试,但一种可能的算法可能是创建第二个列表,并遍历第一个列表。如果项目不在第二个列表中,请将其添加到第二个列表中。

x = [1, 1, 2, 'a', 'a', 3]
y = []
for each in x:
    if each not in y:
        y.append(each)
于 2008-09-18T01:29:11.820 回答
-2

一通。

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
于 2008-09-18T05:08:13.367 回答
-2
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 中的字典是由哈希表实现的。

于 2008-11-11T00:32:18.847 回答