Odstranění duplicit v seznamech

Potřebuji napsat program, který zkontroluje, zda seznam neobsahuje duplikáty, a pokud ano, odstraní je a vrátí nový seznam s položkami, které nebyly duplikovány/odstraněny. Tohle mám, ale upřímně řečeno nevím, co s tím.

def remove_duplicates():
    t = ['a', 'b', 'c', 'd']
    t2 = ['a', 'c', 'd']
    for t in t2:
        t.append(t.remove())
    return t
Řešení

Běžným způsobem, jak získat jedinečnou kolekci položek, je použití set. Sety jsou neuspořádané kolekce odlišných objektů. Chcete-li vytvořit množinu z libovolné iterovatelné položky, můžete ji jednoduše předat vestavěné funkci set(). Pokud budete později opět potřebovat skutečný seznam, můžete podobně předat množinu funkci list().

Následující příklad by měl pokrýt vše, co se snažíte udělat:

>>> t = [1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> t
[1, 2, 3, 1, 2, 5, 6, 7, 8]
>>> list(set(t))
[1, 2, 3, 5, 6, 7, 8]
>>> s = [1, 2, 3]
>>> list(set(t) - set(s))
[8, 5, 6, 7]

Jak vidíte z výsledku příkladu, původní pořadí není zachováno. Jak bylo uvedeno výše, sady samy o sobě jsou neuspořádané kolekce, takže se pořadí ztratí. Při převodu množiny zpět na seznam se vytvoří libovolné pořadí.

Zachování pořadí

Pokud je pro vás pořadí důležité, budete muset použít jiný mechanismus. Velmi častým řešením je spoléhání se na OrderedDict, které udržuje pořadí klíčů při vkládání:

>>> from collections import OrderedDict
>>> list(OrderedDict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Počínaje verzí Python 3.7 je zaručeno, že vestavěný slovník zachovává pořadí vkládání také, takže jej můžete použít i přímo, pokud používáte Python 3.7 nebo novější (nebo CPython 3.6):

>>> list(dict.fromkeys(t))
[1, 2, 3, 5, 6, 7, 8]

Všimněte si, že je to spojeno s režií, která spočívá v tom, že se nejprve vytvoří slovník a pak se z něj vytvoří seznam. Pokud skutečně nepotřebujete zachovat pořadí, je lepší použít množinu. Další podrobnosti a alternativní způsoby zachování pořadí při odstraňování duplicit najdete v tato otázka.


Nakonec si všimněte, že řešení set i OrderedDict/dict vyžadují, aby vaše položky byly hashovatelné. To obvykle znamená, že musí být neměnné. Pokud musíte pracovat s položkami, které nejsou hashovatelné (např. objekty seznamu), pak budete muset použít pomalý přístup, při kterém budete muset v podstatě porovnávat každou položku s každou jinou položkou ve vnořené smyčce.

Komentáře (4)

Stačí jeden řádek: list(set(source_list)).

Set je něco, co nemůže mít duplikáty.

Aktualizace: přístup zachovávající pořadí je dvouřádkový:

from collections import OrderedDict
OrderedDict((x, True) for x in source_list).keys()

Zde využíváme toho, že OrderedDict si pamatuje pořadí vkládání klíčů a nemění ho, když je hodnota u určitého klíče aktualizována. Jako hodnoty vkládáme True, ale mohli bychom vložit cokoli, hodnoty se prostě nepoužívají. (set funguje podobně jako dict s ignorovanými hodnotami.)

Komentáře (3)

Pokud vám na pořadí nezáleží, udělejte toto:

def remove_duplicates(l):
    return list(set(l))

Je zaručeno, že set nebude obsahovat duplikáty.

Komentáře (1)