Удалить все элементы из одного списка, которые присутствуют в другом списке
У меня есть две списка, l1
и l2
. Я хочу выполнить операцию l1 - l2
, которая должна вернуть все элементы списка l1
, которые не присутствуют в l2
.
Я могу предложить наивный подход с использованием цикла, но это будет очень неэффективно. Какой существует питоновский и эффективный способ сделать это?
Например, если у меня есть l1 = [1, 2, 6, 8]
и l2 = [2, 3, 5, 8]
, результатом операции l1 - l2
должно быть [1, 6]
.
5 ответ(ов)
Один из способов — использовать множества (set):
>>> set([1, 2, 6, 8]) - set([2, 3, 5, 8])
set([1, 6])
Однако стоит отметить, что множества не сохраняют порядок элементов и удаляют любые дубликаты. Кроме того, элементы должны быть хэшируемыми. Если эти ограничения приемлемы, то данный метод может быть одним из самых простых и эффективных.
В дополнение к ответу Донота и другим ответам здесь, вы можете получить еще лучшие результаты, используя генераторное выражение вместо обобщенного списка, а также применяя структуру данных set
(поскольку оператор in
работает за O(n) для списка, но за O(1) для множества).
Вот функция, которая вам подойдет:
def filter_list(full_list, excludes):
s = set(excludes)
return (x for x in full_list if x not in s)
Результатом будет итератор, который будет лениво извлекать отфильтрованный список. Если вам нужен реальный объект списка (например, если вам нужно использовать len()
на результате), вы можете легко создать список следующим образом:
filtered_list = list(filter_list(full_list, excludes))
Используйте тип данных set в Python. Это будет наиболее "питонично". 😃
Кроме того, поскольку это встроенное решение, оно должно быть наиболее оптимизированным.
Смотрите:
http://docs.python.org/library/stdtypes.html#set
http://docs.python.org/library/sets.htm (для более старых версий Python)
# Используя литерал множества в Python 2.7.
# В противном случае, используйте: l1 = set([1,2,6,8])
#
l1 = {1, 2, 6, 8}
l2 = {2, 3, 5, 8}
l3 = l1 - l2
Конечно! Ваша проблема заключается в использовании функции reduce
вместе с filter
, чтобы убрать элементы из одного списка, основываясь на элементах другого списка. Однако ваш код имеет некоторые недочеты. Вот как это можно исправить и сделать более читаемым:
from functools import reduce
alternate_solution = reduce(lambda x, y: list(filter(lambda z: z != y, x)), [2, 3, 5, 8], [1, 2, 6, 8])
Но этот код будет возвращать [2, 3, 5, 8], так как ни один элемент из [1, 2, 6, 8]
не содержится в первом списке.
Если вы хотите удалить все элементы из первого списка, которые есть во втором, вот более элегантное решение:
result = list(filter(lambda z: z not in [1, 2, 6, 8], [2, 3, 5, 8]))
Или же, если вам нужно использовать filter
и lambda
, вы можете сделать это так:
result = list(filter(lambda x: x not in [1, 2, 6, 8], [2, 3, 5, 8]))
Оба варианта дадут вам результат: [3, 5]
, так как 2 и 8 будут удалены из первого списка.
Подход с использованием множеств будет оптимальным, если вы действительно хотите такое поведение. Если же ваша задача не заключается в удалении всех экземпляров элементов из списка l1
, которые встречаются только один раз в l2
, то операции с множествами приведут к некорректным результатам. Допустим, у вас есть повторяющиеся элементы в l1
и, возможно, даже в l2
, и вы хотите получить фактическую разницу между двумя списками l1 - l2
, сохраняя при этом порядок оставшихся элементов:
l1 = [1, 2, 3, 4, 5, 5, 6, 5, 5, 2]
l2 = [1, 2, 2, 5]
_ = [l1.remove(item) for item in l2 if item in l1] # игнорируем возвращаемое значение
print(l1) # [3, 4, 5, 6, 5, 5]
- Обратите внимание, что данный способ будет значительно медленнее, чем операции с множествами, поэтому используйте его только в случае необходимости.
- Если вы не хотите изменять оригинальный список, сначала создайте его копию.
Как получить последний элемент списка?
Как клонировать список, чтобы он не изменялся неожиданно после присваивания?
Самый быстрый способ проверить наличие значения в списке
Сравнение: генераторы списков против lambda + filter
Как отсортировать список/кортеж списков/кортежей по элементу на заданном индексе