Например:
>>> x = [1, 1, 2, 'a', 'a', 3]
>>> unique(x)
[1, 2, 'a', 3]
Предположим, что элементы списка хешируются.
Уточнение: Результат должен содержать первый дубликат в списке. Например, [1, 2, 3, 2, 3, 1] становится [1, 2, 3].
Например:
>>> 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)'
И так далее для различных других функций (которые я назвал после их плакатов), у меня есть следующие результаты (на моем первом поколении Intel 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 из них - с списком без дублирования.
Резюме. Прямая реализация с множествами выигрывает над запутанными однострочными: -)
Вот самое быстрое решение до сих пор (для следующего ввода):
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:]
Это на 10% быстрее, чем реализация Аллена, на которой она основана (приурочен к timeit.repeat, JIT, составленному psyco). Он сохраняет первый экземпляр любого дубликата.
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]
Это самый быстрый, сравнивающий все материалы из этого продолжительного обсуждения и другие ответы, приведенные здесь, ссылаясь на это benchmark. Это на 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-лайнер, который использует встроенные функции распознавания списков и dicts.
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() возвращает значение, а также добавляет его в temp dict непосредственно в понимании списка. Использование встроенных функций и хэшей dict будет работать, чтобы максимизировать эффективность процесса.
O (n), если dict is hash, 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
has_key в python - 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)
, я бы воспользовался простым однострочным 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])
Вот два рецепта из документации 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
Если вы вытащите пустой список из вызова set() в ответ Terhost, вы получите небольшое ускорение скорости.
Изменить: found = set ([])
чтобы: found = set()
Однако вам не нужен набор вообще.
def unique(items):
keep = []
for item in items:
if item not in keep:
keep.append(item)
return keep
Используя timeit, я получил следующие результаты:
с множеством ([]) - 4.97210427363
с множеством() - 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]']" - это "секретное имя" создаваемого списка.
Я не знаю, быстро ли это или нет, но, по крайней мере, это просто.
Просто сначала преобразуйте его в набор, а затем снова в список
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
а = [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()
напечатать a
print unique (a)
Вставка элементов примет theta (n) получение, если элемент выходит или нет, будет занимать постоянное время тестирование всех предметов займет также theta (n) поэтому мы можем видеть, что это решение примет theta (n) Помните, что словарь в python, реализованный хэш-таблицей