Удалите повторяющиеся кортежи из списка, если они точно такие же, включая порядок элементов.

Я знаю, что вопросы, подобные этому, задавались много раз в Stack Overflow, но мне нужно удалить повторяющиеся кортежи из списка, но не только если их элементы совпадают, их элементы должны быть в том же порядке. Другими словами, (4,3,5) и (3,4,5) будут присутствовать в выходных данных, а если бы были оба (3,3,5) и (3,3,5), в выходных данных был бы только один.

В частности, мой код:

import itertools

x = [1,1,1,2,2,2,3,3,3,4,4,5]
y = []

for x in itertools.combinations(x,3):
    y.append(x)
print(y)

из которых вывод довольно длинный. Например, в выводе должно быть и (1,2,1), и (1,1,2). Но должен быть только один (1,2,2).


person 5813    schedule 17.10.2013    source источник
comment
Я думаю, вам нужны перестановки, а не комбинации?   -  person sashkello    schedule 17.10.2013
comment
Вы не можете получить (2, 2, 1) из combinations(), учитывая этот ввод. Вместо этого вам нужно будет использовать permutations().   -  person Tim Peters    schedule 17.10.2013
comment
Извините за опечатки, моя ошибка. Но я пробовал использовать permutations() и не получил желаемого результата. Я не хочу (2,2,1) нигде в выводе. Я изменил порядок элементов в своем списке, чтобы дать кортежи, которые я хочу, в том порядке, в котором я хочу, но вопрос остается в силе.   -  person 5813    schedule 17.10.2013
comment
Комбинации @5813 определенно не дадут вам желаемого результата, потому что они не будут включать (1,2,1) и (1,1,2). Заказ вещей - это просто вопрос сортировки, которую можно сделать позже - пожалуйста, смотрите мой ответ.   -  person sashkello    schedule 17.10.2013


Ответы (6)


Об этом позаботится set:

>>> a = [(1,2,2), (2,2,1), (1,2,2), (4,3,5), (3,3,5), (3,3,5), (3,4,5)]
>>> set(a)
set([(1, 2, 2), (2, 2, 1), (3, 4, 5), (3, 3, 5), (4, 3, 5)])
>>> list(set(a))
[(1, 2, 2), (2, 2, 1), (3, 4, 5), (3, 3, 5), (4, 3, 5)]
>>>

set удалит только точные дубликаты.

person Community    schedule 17.10.2013
comment
Имейте в виду, что набор может изменить порядок самого списка - person Ladmerc; 18.04.2017
comment
В Python 3.6+ list(dict.from_key(a)) для поддержания порядка. - person pylang; 17.07.2018
comment
@pylang: я получаю сообщение об ошибке, если использую ваше решение. объект типа 'dict' не имеет атрибута 'from_key'. Мне нужно отсортировать список кортежей, а также сохранить порядок. Я использую Python 3.6.5. - person Raj Mehta; 25.09.2018
comment
dict.fromkeys(). Простите. В частности, list(dict.fromkeys(a)) - person pylang; 25.09.2018
comment
Но он не будет соблюдать порядок, для самого простого и точного решения ознакомьтесь с этим ответом от @roadrunner stackoverflow.com/questions/48300501/ - person Dipen Gajjar; 20.08.2019

Вам нужны уникальные перестановки, а не комбинации:

y = list(set(itertools.permutations(x,3)))

То есть (1,2,2) и (2,1,2) будут считаться одной и той же комбинацией и будет возвращена только одна из них. Однако это разные перестановки. Используйте set() для удаления дубликатов.

Если впоследствии вы хотите отсортировать элементы в каждом кортеже, а также отсортировать весь список, вы можете сделать:

y = [tuple(sorted(q)) for q in y]
y.sort()
person sashkello    schedule 17.10.2013

Не нужно делать for цикл, combinations дает генератор.

x = [1,1,1,2,2,2,3,3,3,4,4,5]
y = list(set(itertools.combinations(x,3)))
person xiaowl    schedule 17.10.2013

Это, вероятно, сделает то, что вы хотите, но это огромное излишество. Это низкоуровневый прототип генератора, который может быть добавлен в itertools когда-нибудь. Это низкий уровень, чтобы упростить повторную реализацию на C. Где N — длина итерируемых входных данных, для наихудшего случая требуется пространство O(N) и выполняется не более 4_ сравнений элементов, независимо от того, сколько анаграмм генерируется. И то, и другое оптимально ;-)

Вы бы использовали его так:

>>> x = [1,1,1,2,2,2,3,3,3,4,4,5]
>>> for t in anagrams(x, 3):
...     print(t)
(1, 1, 1)
(1, 1, 2)
(1, 1, 3)
(1, 1, 4)
(1, 1, 5)
(1, 2, 1)
...

В выводе не будет дубликатов. Примечание: это код Python 3. Для работы под Python 2 требуется несколько изменений.

import operator

class ENode:
    def __init__(self, initial_index=None):
        self.indices = [initial_index]
        self.current = 0
        self.prev = self.next = self

    def index(self):
        "Return current index."
        return self.indices[self.current]

    def unlink(self):
        "Remove self from list."
        self.prev.next = self.next
        self.next.prev = self.prev

    def insert_after(self, x):
        "Insert node x after self."
        x.prev = self
        x.next = self.next
        self.next.prev = x
        self.next = x

    def advance(self):
        """Advance the current index.

        If we're already at the end, remove self from list.

        .restore() undoes everything .advance() did."""

        assert self.current < len(self.indices)
        self.current += 1
        if self.current == len(self.indices):
            self.unlink()

    def restore(self):
        "Undo what .advance() did."
        assert self.current <= len(self.indices)
        if self.current == len(self.indices):
            self.prev.insert_after(self)
        self.current -= 1

def build_equivalence_classes(items, equal):
    ehead = ENode()
    for i, elt in enumerate(items):
        e = ehead.next
        while e is not ehead:
            if equal(elt, items[e.indices[0]]):
                # Add (index of) elt to this equivalence class.
                e.indices.append(i)
                break
            e = e.next
        else:
            # elt not equal to anything seen so far:  append
            # new equivalence class.
            e = ENode(i)
            ehead.prev.insert_after(e)
    return ehead

def anagrams(iterable, count=None, equal=operator.__eq__):
    def perm(i):
        if i:
            e = ehead.next
            assert e is not ehead
            while e is not ehead:
                result[count - i] = e.index()
                e.advance()
                yield from perm(i-1)
                e.restore()
                e = e.next
        else:
            yield tuple(items[j] for j in result)

    items = tuple(iterable)
    if count is None:
        count = len(items)
    if count > len(items):
        return

    ehead = build_equivalence_classes(items, equal)
    result = [None] * count
    yield from perm(count)
person Tim Peters    schedule 17.10.2013

Вы были очень близки. Просто получайте перестановки, а не комбинации. Порядок имеет значение в перестановках, но не в комбинациях. Таким образом, (1, 2, 2) является отличной перестановкой от (2, 2, 1). Однако (1, 2, 2) считается единственной комбинацией одной единицы и двух двоек. Поэтому (2, 2, 1) не считается отличной комбинацией от (1, 2, 2).

Вы можете преобразовать свой список y в набор, чтобы удалить дубликаты...

import itertools

x = [1,1,1,2,2,2,3,3,3,4,4,5]
y = []

for x in itertools.permutations(x,3):
    y.append(x)
print(set(y))

И вуаля, все готово. :)

person Shashank    schedule 17.10.2013

Использование набора set, вероятно, должно работать. Набор — это в основном контейнер, который не содержит повторяющихся элементов.

Python также включает тип данных для наборов. Набор — это неупорядоченная коллекция без повторяющихся элементов. Основные виды использования включают проверку членства и устранение повторяющихся записей. Объекты множества также поддерживают математические операции, такие как объединение, пересечение, разность и симметричная разность.

import itertools

x = [1,1,1,2,2,2,3,3,3,4,4,5]
y = set()

for x in itertools.combinations(x,3):
    y.add(x)
print(y)
person TankorSmash    schedule 17.10.2013
comment
Вы также можете просто сделать набор y и не нужно его преобразовывать, т.е. y = set(); ... ; y.add(x) - person AChampion; 17.10.2013
comment
Может ли кто-нибудь объяснить отрицательный голос? Я бы с радостью улучшил свой ответ. - person TankorSmash; 17.10.2013