Необходимо удалить дубликаты из вложенного списка, сохраняя порядок - PullRequest
0 голосов
/ 08 сентября 2018

У меня есть список, как показано ниже

L = [[1,2],[1,3],[1,4],[2,1],[2,5],[3,1],[3,2]]

Вывод должен быть

[[1,2],[1,3],[1,4],[2,5],[3,2]]

Обратите внимание, что порядок пар и элементов должен быть сохранен. Другими словами, я не могу отсортировать список, потому что порядок элементов должен быть сохранен и для пар. Например, мне нужно сохранить последний элемент [3,2]. Если я сортирую их и удаляю дубликаты, это будет изменено на [2,3], что мне не нужно. Также, когда я говорю, что мне нужно удалить дубликаты, [1,2] или [2,1] считается дубликатом, и я хочу сохранить [1,2] от этого.

Ответы [ 2 ]

0 голосов
/ 08 сентября 2018

Функция 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.

0 голосов
/ 08 сентября 2018

Это работает аналогично удалению дубликатов из обычного списка, сохраняя при этом порядок , но нам нужно что-то сделать, чтобы списки не были хэшируемыми и порядок списков не относился к делу.

Мы можем использовать замороженные наборы, чтобы решить обе эти проблемы одновременно.

>>> lst = [[1,2],[1,3],[1,4],[2,1],[2,5],[3,1],[3,2]] 
>>> seen = set()
>>> result = []
>>> 
>>> for x in lst:
...     s = frozenset(x)
...     if s not in seen:
...         result.append(x)
...         seen.add(s)
... 
>>> result
[[1, 2], [1, 3], [1, 4], [2, 5], [3, 2]]
...