Найти - Пользователи
Полная версия: Удалить дубликаты в списке не нарушив их порядок
Начало » Python для новичков » Удалить дубликаты в списке не нарушив их порядок
1 2 3
tfox
Всем привет. Нашел такие решения.

mylist = 3, 2, 2, 1
mylist = list(set(mylist))
mylist = dict(zip(mylist, mylist)).values()
К сожалению они не только удаляют повторяющиеся элементы но и сортируют сам список

Исходный список: mylist = 3, 2, 2, 1
Необходимый результат: 3, 2, 1
reclosedev
Создаем множество, в котором будут хранится уникальные элементы. Пробегаемся по списку, добавляя элементы в множество, если элемента еще нет в множестве, добавляем в результат.
def unique(lst):
    seen = set()
    result = []
    for x in lst:
        if x in seen:
            continue
        seen.add(x)
        result.append(x)
    return result

Баловство:
def unique(lst):
    seen = set()
    return zip(*[(x, seen.add(x)) for x in lst if x not in seen])[0]
EBFE
>>> from collections import OrderedDict
>>> mylist = 3, 2, 2, 1
>>> d = OrderedDict.fromkeys(mylist,None)
>>> new_list = d.keys()
>>> new_list
[3, 2, 1]
>>> collect = set()
>>> new_list = [e for e in mylist if e not in collect and not collect.add(e)]
>>> new_list
[3, 2, 1]
почему 2 вариант работает:
>>> print collect.add(1)
None
>>> print collect.add(10)
None
>>> not None
True
т.e “if e not in collect and True”, только с добавлением в set. И да, второй вариант имхо грязноват для применения в проектах.

Edit: опоздал
reclosedev
EBFE
Edit: опоздал
Почему, интересные варианты
odnochlen
EBFE, ленивые вычисления используешь? Для функциональных языков - нормально, для питона - действительно грязновато

Для улучшения читаемости можно
and (collect.add(e) or True)
reclosedev
OrderedDict медленно работает:
import timeit
import random
from collections import OrderedDict
 
def bench_set_loop(lst):
    seen = set()
    result = []
    for x in lst:
        if x in seen:
            continue
        seen.add(x)
        result.append(x)
    return result
 
def bench_set_zip(lst):
    seen = set()
    return zip(*[(x, seen.add(x)) for x in lst if x not in seen])[0]
 
def bench_set_compr(lst):
    seen = set()
    return [e for e in lst if e not in seen and not seen.add(e)]
 
def bench_od(lst):
    d = OrderedDict.fromkeys(mylist,None)
    return d.keys()
 
N = 10000
for elements in (10, 100, 1000):
    mylist = [random.randint(0, 20) for i in range(elements)]
    print elements, len(set(mylist))
    for name, func in [(k, v) for k, v in globals().items()
                       if k.startswith('bench')]:
        print "  %-20s %s" % (name, timeit.timeit(lambda: func(mylist), number=N))

10 9
bench_set_zip 0.0524770499977
bench_set_loop 0.0440655407095
bench_od 0.297880415579
bench_set_compr 0.0362228060105
100 21
bench_set_zip 0.146828339709
bench_set_loop 0.133538776531
bench_od 1.10920786354
bench_set_compr 0.117137887161
1000 21
bench_set_zip 0.695040802332
bench_set_loop 0.687664888766
bench_od 8.29270173866
bench_set_compr 0.666427844187
tfox
Спасибо.
Примерно такой вариант возьму на вооружение.

def unique(lst):
    seen = set()
    result = []
    for x in lst:
        if x in seen:
            continue
        seen.add(x)
        result.append(x)
    return result

Думал, что для такой тривиальной задачи имеется стандартная питоновская функция
tfox
>>> collect = set()
>>> new_list = [e for e in mylist if e not in collect and not collect.add(e)]
>>> new_list
[3, 2, 1]

Здесь черт ногу поломает
EBFE
odnochlen
EBFE, ленивые вычисления используешь? Для функциональных языков - нормально, для питона - действительно грязновато
Не, грязновато из-за этакого неявного побочного эффекта.
set.add вроде-бы ничего не возвращает, но это “ничего” все равно используется. Вот если бы set.add выдавал True или False в зависимости от того, был ли добавлен элемент, было бы нормально
Кстати - от “ленивости” правильность выполнения не зависит - все будет нормально работать, даже если “collect.add” будет каждый раз вызыватся. “Lazyness” в вычислениях логических выражений есть по моему в почти всех более менее распростроненных языках (правда злоупотреблять все-таки не стоит)
http://en.wikipedia.org/wiki/Short-circuit_evaluation


что интересно
def bench_discard_set(lst):
    coll = set(lst)
    return [e for e in lst if e in coll and not coll.discard(e)]
def bench_filter(lst):
    coll = set()
    return filter(lambda e: e not in coll and not coll.add(e), lst)
10 9
  bench_od             0.405310646121
  bench_set_compr      0.033174696845
  bench_set_zip        0.0523766024844
  bench_filter         0.0495791956387
  bench_set_loop       0.0485148183261
  bench_discard_set   0.0392787948708
100 21
  bench_od             1.3885027233
  bench_set_compr      0.113566557072
  bench_set_zip        0.151898768286
  bench_filter         0.23770336247
  bench_set_loop       0.147533089046
  bench_discard_set   0.157861975861
1000 21
  bench_od             9.86658521197
  bench_set_compr      0.642895445043
  bench_set_zip        0.692988929321
  bench_filter         1.80101418028
  bench_set_loop       0.722098975156
  bench_discard_set   1.11761196075
odnochlen
EBFE
Кстати - от “ленивости” правильность выполнения не зависит - все будет нормально работать, даже если “collect.add” будет каждый раз вызыватся.
Правильность не зависит, зависит скорость.

tfox
Думал, что для такой тривиальной задачи имеется стандартная питоновская функция
Можно сказать, что она есть (см. OrderedDict).

А вообще, для удаления дупликатов есть два подхода:
- Сохранение элементов в hashset, проверка и отбрасывание повторов. Поиск в hashmap ~ O(1), в сумме дает ~O(n) по времени и O(размер уникальных элементов) по памяти. Бонус - метод можно использовать для исключение одного списка из другого и последовательность элементов сохраняется. Если список для исключения не вмещается в память - хз.
- Cортировка с последующим исключением одинаковых соседних элементов. O(n*log n) по времени и O(n) по памяти (сортировку можно делать на месте), причем коэффициент меньше, чем в первом случае. При большом проценте уникальных элементов занимает меньше памяти, чем первый вариант. Бонус - можно сортировать частями, даже если весь массив не вмещается в память.

И да, ФП (map/списковые выражения) таки быстрее циклов.

>>> collect = set()
>>> new_list = [e for e in mylist if e not in collect and not collect.add(e)]
>>> new_list
[3, 2, 1]
А не нарушает ли это концепцию ФП (функция получается грязной, результат для одного элемента зависит от другого)?
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB