Функция unique_everseen
в рецептах itertools
в документации делает именно то, что вам нужно:
>>> lst = [[1,2],[1,3],[1,4],[2,1],[2,5],[3,1],[3,2]]
>>> list(unique_everseen(lst, key=frozenset))
[[1, 2], [1, 3], [1, 4], [2, 5], [3, 2]]
Основная идея состоит в том, что он сохраняет набор всех значений, которые были просмотрены, и пропускает все значения, которые уже есть в наборе.
Функция key
работает так же, как в sort
, max
и т. Д., Как описано в Сортировка HOWTO . Вы хотите создать два списка, которые имеют одинаковые значения в другом порядке соответствия, поэтому нам нужно сравнить набор значений каждого списка, а не сами списки. (Причина, по которой нам нужно frozenset
вместо set
, заключается в том, что set
является изменяемым и поэтому не может храниться в наборе.)
Если бы в ваших подсписках было более 2 элементов, вопрос был бы неоднозначным. Если у вас есть, скажем, [1, 1, 2]
и [1, 2, 2]
, хотите ли вы, чтобы их считали дублирующими или нет?
- Если да, то вы рассматриваете их как набор, так что используйте
key=frozenset
.
- Если нет: тогда вы рассматриваете их как мультимножество. Самая хорошая реализация мультимножеств на Python -
collections.Counter
, но FrozenCounter
нет (и сборка только для этой цели, вероятно, излишняя). Вы можете смоделировать один из нескольких способов:
key=lambda sublist: frozenset(Counter(sublist).items())
key=lambda sublist: sorted(Counter(sublist).items())
key=lambda sublist: tuple(sorted(sublist))
Поскольку ваша первоначальная мысль состояла в том, чтобы отсортировать подсписки - что было неприемлемо только потому, что вам нужно получить исходное значение, а не отсортированное значение, - я думаю, что последний из этих вариантов, скорее всего, будет тем, который вам нужен хочу, но это все еще только предположение.
Вы можете просто скопировать и вставить функцию из документа в ваш код:
from itertools import *
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 filterfalse(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
… или установите стороннюю библиотеку more_itertools
и используйте ее unique_everseen
оттуда. Или другая сторонняя библиотека toolz
имеет эквивалентную функцию с именем unique
.