Удалите все элементы, которые встречаются в одном списке из другого

Скажем, у меня есть два списка, l1 и l2. Я хочу выполнить l1 - l2, который возвращает все элементы l1 не в l2.

Я могу придумать метод наивного цикла для этого, но это будет действительно неэффективно. Что такое питонический и эффективный способ сделать это?

В качестве примера, если у меня есть l1 = [1,2,6,8] and l2 = [2,3,5,8], l1 - l2 должен возвращать [1,6]

Ответ 1

В Python есть языковая функция Список пониманий, которая идеально подходит для того, чтобы сделать подобные вещи чрезвычайно простыми. Следующий оператор делает именно то, что вам нужно, и сохраняет результат в l3:

l3 = [x for x in l1 if x not in l2]

l3 будет содержать [1, 6].

Ответ 2

Один из способов - использовать наборы:

>>> set([1,2,6,8]) - set([2,3,5,8])
set([1, 6])

Ответ 3

Развернув ответ на Donut и другие ответы здесь, вы можете получить еще лучшие результаты, используя понимание генератора вместо понимания списка, и используя структуру данных 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))

Ответ 4

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

>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])

#     v  'filter' returns the a iterator object. Here I'm type-casting 
#     v  it to 'list' in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]

Сравнение производительности

Здесь я сравниваю эффективность всех ответов, упомянутых здесь. Как и ожидалось, операция на основе Arkku set является самой быстрой.

PS: set не поддерживает порядок и удаляет повторяющиеся элементы из списка. Следовательно, не используйте набор разностей, если вам нужно что-то из этого.

Ответ 5

Используйте тип набора Python. Это было бы самым пифоническим.:)

Кроме того, поскольку он является родным, он также должен быть самым оптимизированным методом.

См:

http://docs.python.org/library/stdtypes.html#set

http://docs.python.org/library/sets.htm (для более старого python)

# Using Python 2.7 set literal format.
# Otherwise, use: l1 = set([1,2,6,8])
#
l1 = {1,2,6,8}
l2 = {2,3,5,8}
l3 = l1 - l2

Ответ 6

Альтернативное решение:

reduce(lambda x,y : filter(lambda z: z!=y,x) ,[2,3,5,8],[1,2,6,8])

Ответ 7

используйте Установить понимание, чтобы получить набор, затем используйте Список пониманий, чтобы получить список

l2set = {x for x in l2}
l3 = [x for x in l1 if x not in l2set]

код теста производительности:

import time

l1 = list(range(1000*10 * 3))
l2 = list(range(1000*10 * 2))

l2set = {x for x in l2}

tic = time.time()
l3 = [x for x in l1 if x not in l2set]
toc = time.time()
diffset = toc-tic
print(diffset)

tic = time.time()
l3 = [x for x in l1 if x not in l2]
toc = time.time()
difflist = toc-tic
print(difflist)

print("speedup %fx"%(difflist/diffset))

результат теста производительности:

0.0015058517456054688
3.968189239501953
speedup 2635.179227x