Как избавиться от повторов в списке Python без потери последовательности элементов
Практическая задача: удаление дубликатов из списка с сохранением порядка
Как эффективно удалить повторяющиеся элементы из списка, сохранив порядок первых вхождений?
В Python 3.7 и новее словари сохраняют порядок вставки. Поэтому самый лаконичный и быстрый способ - использовать dict.fromkeys(). Этот метод создаёт словарь, где ключами становятся элементы списка, а значения игнорируются. Порядок ключей сохраняется, а дубликаты автоматически отбрасываются. Результат преобразуется обратно в список.
original = [3, 1, 2, 1, 5, 3, 4, 2]
unique = list(dict.fromkeys(original))
print(unique)
Python set (тип set в python)
[3, 1, 2, 5, 4]
практические задания по python (практические задания по python)
Цель: получение нового списка с уникальными элементами, сохранив их исходный порядок. Подходит для любых хешируемых типов данных (числа, строки, кортежи).
Как удалить дубликаты, используя вспомогательное множество?
Классический итеративный метод с множеством (set) для отслеживания уже встреченных элементов. Он гарантирует порядок и работает в любой версии Python. Сложность O(n).
original = [3, 1, 2, 1, 5, 3, 4, 2]
seen = set()
unique = []
for item in original:
if item not in seen:
seen.add(item)
unique.append(item)
print(unique)
уроки python с заданиями (уроки python с заданиями)
[3, 1, 2, 5, 4]
Какое решение подходит для версий Python до 3.7?
Если нужна совместимость со старыми версиями (до 3.7), где порядок словарей не гарантируется, используется collections.OrderedDict.
from collections import OrderedDict
original = [3, 1, 2, 1, 5, 3, 4, 2]
unique = list(OrderedDict.fromkeys(original))
print(unique)
[3, 1, 2, 5, 4]
Как поступить, если элементы списка не являются хешируемыми (например, вложенные списки)?
Для нехешируемых объектов (списки, словари) нельзя использовать set или dict. Тогда применяется полный перебор с проверкой in для нового списка. Это медленнее (O(n^2)), но универсально.
original = [[1,2], [3,4], [1,2], [5]]
unique = []
for item in original:
if item not in unique:
unique.append(item)
print(unique)
[[1, 2], [3, 4], [5]]
Цели: выбор метода зависит от хешируемости данных, требуемой производительности и версии Python. Для больших списков хешируемых элементов предпочтительнее set или dict, для небольших или нехешируемых - цикл с проверкой in.
Типичные ошибки и их решения:
- Потеря порядка при использовании set напрямую:
list(set(original)). Решение: использовать методы, сохраняющие порядок. - Изменение списка во время итерации - не следует удалять элементы из исходного списка внутри цикла. Лучше создавать новый список.
- Ошибка TypeError при попытке добавить нехешируемый объект в set. Решение: использовать проверку
in uniqueили преобразовать объект к хешируемому типу (например, кортежу). - Медленная работа на больших списках при проверке
in unique(O(n^2)). Решение: использовать вспомогательное множество для хешируемых элементов.
Как удалить дубликаты, используя pandas (для анализа данных)?
Если в проекте уже используется библиотека pandas, можно применить метод drop_duplicates(). Подходит для числовых массивов и Series.
import pandas as pd
original = [3, 1, 2, 1, 5, 3, 4, 2]
unique = pd.Series(original).drop_duplicates().tolist()
print(unique)
[3, 1, 2, 5, 4]
Цель: интеграция с пайплайнами обработки данных, когда списки являются частью DataFrame. Метод сохраняет порядок по умолчанию.
Продвинутые и нераспространённые методы удаления дубликатов
1. Использование itertools.groupby после сортировки по индексу
Сначала создаётся список кортежей (индекс, элемент), затем сортировка по элементу, группировка, выбор первого вхождения каждой группы и восстановление порядка по индексу. Метод не требует вспомогательной памяти, но сложность O(n log n) из-за сортировки.
from itertools import groupby
original = [3, 1, 2, 1, 5, 3, 4, 2]
indexed = list(enumerate(original))
indexed.sort(key=lambda x: x[1])
grouped = groupby(indexed, key=lambda x: x[1])
first_occurrences = [next(g) for _, g in grouped]
first_occurrences.sort(key=lambda x: x[0])
unique = [item for _, item in first_occurrences]
print(unique)
[3, 1, 2, 5, 4]
2. Использование numpy.unique с возвратом индексов
Для числовых массивов библиотека numpy предоставляет функцию unique с параметром return_index, которая возвращает индексы первых вхождений уникальных элементов. После сортировки этих индексов восстанавливается порядок.
import numpy as np
original = [3, 1, 2, 1, 5, 3, 4, 2]
_, indices = np.unique(original, return_index=True)
unique = [original[i] for i in sorted(indices)]
print(unique)
[3, 1, 2, 5, 4]
3. Рекурсивное удаление дубликатов из вложенных списков
Если список содержит вложенные структуры, можно написать рекурсивную функцию, которая обходит все уровни, сохраняя порядок, и удаляет дубликаты на каждом уровне. Для сравнения вложенных списков они временно преобразуются в кортежи (хешируемые).
def dedup_nested(lst):
seen = set()
result = []
for item in lst:
if isinstance(item, list):
# рекурсивно обрабатываем вложенный список
processed = dedup_nested(item)
# хешируем как кортеж для сравнения
key = tuple(processed)
if key not in seen:
seen.add(key)
result.append(processed)
else:
if item not in seen:
seen.add(item)
result.append(item)
return result
nested = [[1,2], [3,4], [1,2], [5, [1,2]], [5, [1,2]]]
print(dedup_nested(nested))
[[1, 2], [3, 4], [5, [1, 2]]]
4. Использование декоратора с мемоизацией для ускорения повторных вызовов
Можно создать декоратор, который кэширует результаты предыдущих вызовов. Применительно к удалению дубликатов: если один и тот же список обрабатывается несколько раз, декоратор вернёт сохранённый результат. Показано на примере функции dedup.
from functools import lru_cache
def dedup(lst):
seen = set()
result = []
for item in lst:
if item not in seen:
seen.add(item)
result.append(item)
return result
# Оборачиваем в lru_cache (список должен быть преобразован в кортеж для хеширования)
@lru_cache(maxsize=None)
def cached_dedup(tpl):
return tuple(dedup(list(tpl)))
original = (3, 1, 2, 1, 5, 3, 4, 2)
unique = list(cached_dedup(original))
print(unique)
[3, 1, 2, 5, 4]
5. Использование more_itertools.unique_everseen
Библиотека more-itertools содержит функцию unique_everseen, которая возвращает итератор, выдающий уникальные элементы в порядке их первого появления. Устанавливается через pip install more-itertools.
from more_itertools import unique_everseen
original = [3, 1, 2, 1, 5, 3, 4, 2]
unique = list(unique_everseen(original))
print(unique)
[3, 1, 2, 5, 4]