Python: как удалить дубликаты из списка и сохранить порядок

Категория: Python · Язык: Python · Обновлено:

Самый короткий способ удалить дубликаты из списка в Python с сохранением порядка: list(dict.fromkeys(items)). Если порядок не важен, подойдёт list(set(items)). Для списков словарей нужна отдельная функция.

Быстрые варианты

Python
items = [3, 1, 3, 2, 1, 5]

# 1. Сохраняет порядок первого появления (Python 3.7+)
unique = list(dict.fromkeys(items))
print(unique)  # [3, 1, 2, 5]

# 2. Порядок не гарантирован
unique = list(set(items))

# 3. Отсортированный результат без повторов
unique = sorted(set(items))

Способ с dict.fromkeys работает потому, что ключи словаря уникальны, а начиная с Python 3.7 словарь помнит порядок вставки.

Когда какой способ использовать

Задача

Решение

Порядок сохраняется

Элементы хешируемые (числа, строки, кортежи)

list(dict.fromkeys(items))

Да

Порядок не нужен, важна скорость

list(set(items))

Нет

Нужен отсортированный результат

sorted(set(items))

По значению

Элементы — списки или словари

Функция с seen (ниже)

Да

Дубликаты определяются по одному полю

Функция с ключом (ниже)

Да

Дубликаты в списке словарей или списков

Списки и словари нельзя положить в set: они изменяемые, поэтому нехешируемые. Вместо этого запоминайте уже встреченные значения.

Python
def unique_by(items, key=lambda x: x):
    """Удаляет дубликаты, сохраняя порядок первого появления."""
    seen = set()
    result = []
    for item in items:
        marker = key(item)
        if marker not in seen:
            seen.add(marker)
            result.append(item)
    return result


users = [
    {"id": 1, "name": "Анна"},
    {"id": 2, "name": "Борис"},
    {"id": 1, "name": "Анна"},
]

# дубликаты определяем по полю id
print(unique_by(users, key=lambda u: u["id"]))
# [{'id': 1, 'name': 'Анна'}, {'id': 2, 'name': 'Борис'}]

# список списков: превращаем во вложенный кортеж
rows = [[1, 2], [3, 4], [1, 2]]
print(unique_by(rows, key=tuple))  # [[1, 2], [3, 4]]

Удалить дубликаты на месте

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

Python
items = [3, 1, 3, 2, 1, 5]
items[:] = dict.fromkeys(items)
print(items)  # [3, 1, 2, 5]

Без учёта регистра

Для строк используйте ключ str.casefold: в результате останется первое написание.

Python
tags = ["Python", "python", "SQL", "sql", "Git"]
print(unique_by(tags, key=str.casefold))  # ['Python', 'SQL', 'Git']

Частые вопросы

Почему list(set(items)) меняет порядок?

Множество (set) не хранит порядок элементов. Чтобы его сохранить, используйте dict.fromkeys.

Как найти повторяющиеся элементы, а не удалить их?

Посчитайте элементы через collections.Counter и оставьте те, что встречаются больше одного раза:

Python
from collections import Counter

items = ["a", "b", "a", "c", "b"]
duplicates = [value for value, count in Counter(items).items() if count > 1]
print(duplicates)  # ['a', 'b']

Работает ли dict.fromkeys в старых версиях Python?

Порядок ключей гарантируется языком с версии 3.7. В CPython 3.6 он тоже сохраняется, но как деталь реализации.