2009-10-11 26 views
20

Jest to w rzeczywistości rozszerzenie tego pytania. Odpowiedzi na to pytanie nie zachowały "porządku" listy po usunięciu duplikatów. How to remove these duplicates in a list (python)Usuwanie duplikatów z listy przy zachowaniu jej kolejności (Python)

biglist = 

[ 

    {'title':'U2 Band','link':'u2.com'}, 
    {'title':'Live Concert by U2','link':'u2.com'}, 
    {'title':'ABC Station','link':'abc.com'} 

] 

W tym przypadku, 2. element powinien zostać usunięty, ponieważ poprzednie „u2.com” Element już istnieje. Jednak należy zachować porządek.

Odpowiedz

23

Moja odpowiedź na inne pytanie, które całkowicie ignorowane !, pokazuje mylisz się twierdząc, że

odpowiedzi tej kwestii nie utrzymania „porządku”

  • moja odpowiedź zrobił zachować porządek, a to wyraźnie powiedział to zrobił. Tutaj jest to kolejny, z dodatkiem naciskiem, aby zobaczyć, czy można po prostu zachować ignorując go ...:

Prawdopodobnie najszybszy podejście, o naprawdę duże liście, jeśli chcesz zachować dokładną kolejność elementów które pozostają, jest następujące:

biglist = [ 
    {'title':'U2 Band','link':'u2.com'}, 
    {'title':'ABC Station','link':'abc.com'}, 
    {'title':'Live Concert by U2','link':'u2.com'} 
] 

known_links = set() 
newlist = [] 

for d in biglist: 
    link = d['link'] 
    if link in known_links: continue 
    newlist.append(d) 
    known_links.add(link) 

biglist[:] = newlist 
+3

Hej, Alex, z ciekawości, dlaczego umieściłeś [:] po lewej stronie zadania? Zwykle widziałem to na RHS. Czy to tylko osobiste preferencje? Patrząc na to na początku, nie byłem nawet pewien, co by to zrobiło, haha. – xitrium

+2

@xitrium Użycie '[:]' po lewej stronie zastąpiło wszystkie pozycje na liście, zamiast samej listy. Może to mieć wpływ np. jeśli robisz to wewnątrz funkcji z listą, która jest przekazywana: jeśli * zmienisz * listę zmieniła ona poza funkcją, jeśli * zastąpisz *, wtedy nie ma to wpływu na zewnętrzną listę). W tym konkretnym przypadku nie widzę żadnego zauważalnego efektu. – Mark

0

super łatwy sposób to zrobić, to:

def uniq(a): 
    if len(a) == 0: 
     return [] 
    else: 
     return [a[0]] + uniq([x for x in a if x != a[0]]) 

Nie jest to najbardziej skuteczny sposób, ponieważ:

  • przeszukuje całej listy dla każdego elementu na liście, więc to o (n^2)
  • to rekurencyjny to wykorzystuje zagnieżdżenia równy długości listy

Jednak dla prostych zastosowań (nie więcej niż kilkaset pozycji, nie krytycznych pod względem wydajności) jest to wystarczające.

+0

Czy każdy może zaproponować sposób skalowania? – TIMEX

+0

Odpowiedź Kalmi wskazuje na wiele dobrych rozwiązań. –

3

Ta strona omawia różne metody i ich prędkości: http://www.peterbe.com/plog/uniqifiers-benchmark

* Zalecana metoda:

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 

f5(biglist,lambda x: x['link']) 

* od tej strony

1
dups = {} 
newlist = [] 
for x in biglist: 
    if x['link'] not in dups: 
     newlist.append(x) 
     dups[x['link']] = None 

print newlist 

produkuje

[{'link': 'u2.com', 'title': 'U2 Band'}, {'link': 'abc.com', 'title': 'ABC Station'}] 

Zauważ, że tutaj użyłem słownika. Dzięki temu test jest znacznie bardziej wydajny niż przy użyciu listy.

+1

Mylisz się, że sprawdzenie w dyktaturze jest szybsze niż w zestawie (listy to zupełnie inna sprawa). –

+0

ok, naprawione, dzięki. Domyślam się, że set jest prawdopodobnie zaimplementowany z hashem. – Peter

0

Myślę, że używanie zestawu powinno być całkiem skuteczne.

seen_links = set() 
for index in len(biglist): 
    link = biglist[index]['link'] 
    if link in seen_links: 
     del(biglist[index]) 
    seen_links.add(link) 

myślę, że to powinno wejść w O (nlog (n))

+2

w rzeczywistości jest to O (n^2), ponieważ del na liście jest O (n) –

+0

Tak jest. Dzięki! – ABentSpoon

7

Generatory są świetne.

def unique(seq): 
    seen = set() 
    for item in seq: 
     if item not in seen: 
      seen.add(item) 
      yield item 

biglist[:] = unique(biglist) 
+0

To jest to, czego potrzebowałem w moim problemie. Sugerowałbym uczynienie go bardziej ogólnym dodając 'key = lambda item: item' do sygnatury metody. Następnie użyj 'klucz (element)' dla zestawu. – Harvey

1
list = ['aaa','aba','aaa','aea','baa','aaa','aac','aaa',] 

uniq = [] 

for i in list: 
       if i not in uniq: 
        uniq.append(i) 

print list 

print uniq 

wyjściowy będzie:

[ 'A A', 'ABA', 'A A', 'AEA', 'Baa', 'aaa', 'AAC', 'aaa' ]

[ 'aaa', 'aba', 'AEA', 'baa', 'aac']

1

jest to elegancki i kompaktowy sposób, z listowego (ale nie tak skuteczny jak w słowniku) :

mylist = ['aaa','aba','aaa','aea','baa','aaa','aac','aaa',] 

[ v for (i,v) in enumerate(mylist) if v not in mylist[0:i] ] 

A w kontekście odpowiedzi:

[ v for (i,v) in enumerate(biglist) if v['link'] not in map(lambda d: d['link'], biglist[0:i]) ] 
29

użycie set(), a następnie ponownego sortowania za pomocą indeksu oryginalnej listy.

>>> mylist = ['c','a','a','b','a','b','c'] 
>>> sorted(set(mylist), key=lambda x: mylist.index(x)) 
['c', 'a', 'b'] 
+0

To jest fantastyczne! Dokładnie to, czego szukałem. Czy mógłbyś wyjaśnić, jak to działa? (z wykorzystaniem lambda itp.) Dzięki –

+1

Neat kod Pythona. Wadą jest to, że powoduje dodatkowy sort, a więc niepotrzebny O (n * log (n)) (gdzie inaczej wystarczyłoby O (n)). – yprez

+0

może być schludny, pod względem wydajności dalekim od optymalnego – Yerken