I have two lists:
big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]
I want to remove all sub_list occurrences in big_list.
result
You'd have to implement it yourself. Here is the basic idea:
def remove_sublist(lst, sub):
i = 0
out = []
while i < len(lst):
if lst[i:i+len(sub)] == sub:
i += len(sub)
else:
out.append(lst[i])
i += 1
return out
This steps along every element of the original list and adds it to an output list if it isn't a member of the subset. This version is not very efficient, but it works like the string example you provided, in the sense that it creates a new list not containing your subset. It also works for arbitrary element types as long as they support ==
. Removing [1,1,1]
from [1,1,1,1]
will correctly result in [1]
, as for a string.
Here is an IDEOne link showing off the result of
>>> remove_sublist([1, 'a', int, 3, float, 'a', int, 5], ['a', int])
[1, 3, <class 'float'>, 5]
A recursive approach:
def remove(lst, sub):
if not lst:
return []
if lst[:len(sub)] == sub:
return remove(lst[len(sub):], sub)
return lst[:1] + remove(lst[1:], sub)
print(remove(big_list, sub_list))
This outputs:
[2, 3, 4]
You can use recursion with a generator:
def remove(d, sub_list):
if d[:len(sub_list)] == sub_list and len(sub_list) <= len(d[:len(sub_list)]):
yield from [[], remove(d[len(sub_list):], sub_list)][bool(d[len(sub_list):])]
else:
yield d[0]
yield from [[], remove(d[1:], sub_list)][bool(d[1:])]
tests = [[[2, 1, 2, 3, 1, 2, 4], [1, 2]], [[1, 2, 1, 2], [1, 2]], [[1, 'a', int, 3, float, 'a', int, 5], ['a', int]], [[1, 1, 1, 1, 1], [1,1,1]]]
for a, b in tests:
print(list(remove(a, b)))
Output:
[2, 3, 4]
[]
[1, 3, <class 'float'>, 5]
[1, 1]
Just for fun, here is the closest approximation to a one-liner:
from functools import reduce
big_list = [2, 1, 2, 3, 1, 2, 4]
sub_list = [1, 2]
result = reduce(lambda r, x: r[:1]+([1]+r[2:-r[1]],[min(len(r[0]),r[1]+1)]+r[2:])[r[-r[1]:]!=r[0]]+[x], big_list+[0], [sub_list, 1])[2:-1]
Don't trust that it works? Check it on IDEone!
Of course it's far from efficient and is disgustfully cryptic, however it should help to convince the OP to accept @Mad Physicist's answer.
Use itertools.zip_longest
to create n element tuples (where n is length of sub_list) and then filter the current element and next n-1 elements when one of the element matched the sub_list
>>> from itertools import zip_longest, islice
>>> itr = zip_longest(*(big_list[i:] for i in range(len(sub_list))))
>>> [sl[0] for sl in itr if not (sl == tuple(sub_list) and next(islice(itr, len(sub_list)-2, len(sub_list)-1)))]
[2, 3, 4]
To improve the efficiency, you can calculate tuple(sub_list)
and len(sub_list)
before hand you start filtering
>>> l = len(sub_list)-1
>>> tup = tuple(sub_list)
>>> [sl[0] for sl in itr if not (sl == tup and next(islice(itr, l-1, l)))]
[2, 3, 4]
A improved version to check whether lst[i:i+len(sub)] < len(lst)
def remove_sublist(lst, sub):
i = 0
out = []
sub_len = len(sub)
lst_len = len(lst)
while i < lst_len:
if (i+sub_len) < lst_len:
if lst[i: i+sub_len] == sub:
i += sub_len
else:
out.append(lst[i])
i += 1
else:
out.append(lst[i])
i += 1
return out