2009-10-11 12 views
20

Ceci est en fait une extension de cette question. Les réponses à cette question n'ont pas conservé l '"ordre" de la liste après suppression des doublons. How to remove these duplicates in a list (python)Supprimer les doublons dans une liste tout en gardant sa commande (Python)

biglist = 

[ 

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

] 

Dans ce cas, le 2ème élément doit être retiré parce qu'un élément précédent « u2.com » existe déjà. Cependant, la commande doit être conservée.

Répondre

23

Ma réponse à votre autre question, que vous complètement ignoré !, montre que vous êtes tort de prétendre que

les réponses de cette question ne garder "l'ordre"

  • ma réponse fait garder l'ordre, et il clairement a dit il a fait. Ici, il est encore une fois, avec un accent supplémentaire pour voir si vous pouvez garder l'ignorer ...:

Probablement la méthode la plus rapide pour une liste vraiment grand, si vous voulez préserver l'ordre exact des éléments qui restent, est la suivante ...:

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

Hey Alex, par curiosité pourquoi mettez-vous le [:] sur le côté gauche de la mission? Je l'ai habituellement vu sur le RHS. Est-ce juste une préférence personnelle? En y regardant d'abord, je n'étais même pas sûr de ce que ça ferait, haha. – xitrium

+2

@xitrium L'utilisation de '[:]' à gauche a remplacé tous les éléments de la liste, au lieu de la liste elle-même. Cela pourrait avoir un effet, par ex. si vous faites ceci dans une fonction avec une liste qui est transmise: si vous * changez * la liste est changée en dehors de la fonction, si vous la * remplacez * alors la liste extérieure n'est pas affectée). Dans ce cas particulier, il n'y a aucun effet observable que je peux voir. – Mark

0

Un super moyen facile de le faire est:

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

Ce n'est pas la façon la plus efficace, parce que:

  • il recherche dans toute la liste pour chaque élément dans la liste, il est O (n^2)
  • il est récursive si utilise un profondeur de la pile égale à la longueur de la liste

Cependant, pour des utilisations simples (pas plus de quelques centaines d'éléments, pas de performance critique), cela suffit.

+0

Quelqu'un peut-il trouver une solution évolutive? – TIMEX

+0

La réponse de Kalmi indique un certain nombre de bonnes solutions. –

3

Cette page traite de différentes méthodes et leurs vitesses: http://www.peterbe.com/plog/uniqifiers-benchmark

La méthode recommandée *:

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']) 

* par cette page

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

print newlist 

produit

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

Notez que j'ai utilisé un dictionnaire ici. Cela rend le test not in dups beaucoup plus efficace que l'utilisation d'une liste.

+1

Vous avez tort de vérifier que la dict est plus rapide que dans un ensemble (les listes sont complètement différentes). –

+0

ok, corrigé, merci. Je suppose que set est probablement implémenté avec un hash. – Peter

0

Je pense que l'utilisation d'un ensemble devrait être assez efficace.

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

Je pense que cela devrait venir à O (nlog (n))

+2

en fait c'est O (n^2) parce que del sur une liste est O (n) –

+0

Donc c'est. Merci! – ABentSpoon

7

les générateurs sont grands.

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

biglist[:] = unique(biglist) 
+0

C'est ce dont j'avais besoin pour mon problème. Je suggère de le rendre plus générique en ajoutant 'key = lambda item: item' à la signature de la méthode. Ensuite, utilisez 'key (item)' pour l'ensemble. – 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 

sortie sera:

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

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

1

Ceci est une façon élégante et compacte, avec la compréhension de la liste (mais pas aussi efficace avec le dictionnaire) :

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

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

Et dans le contexte de la réponse:

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

set utilisation(), puis re-trier en utilisant l'index de la liste initiale.

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

C'est fantastique! Exactement ce que je cherchais. Cela vous dérangerait-il d'expliquer comment cela fonctionne? (avec l'utilisation de lambda etc) Merci –

+1

Neat Python code. L'inconvénient est qu'il provoque un tri supplémentaire, donc un O (n * log (n)) inutile (où sinon O (n) serait suffisant). – yprez

+0

pourrait être propre, performance-sage loin d'être optimale – Yerken

Questions connexes