How to remove every occurrence of sub-list from list

前端 未结 13 607
走了就别回头了
走了就别回头了 2021-01-07 16:16

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

相关标签:
13条回答
  • 2021-01-07 16:46

    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]
    
    0 讨论(0)
  • 2021-01-07 16:49

    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]
    
    0 讨论(0)
  • 2021-01-07 16:49

    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]
    
    0 讨论(0)
  • 2021-01-07 16:49

    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.

    0 讨论(0)
  • 2021-01-07 16:51

    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]
    
    0 讨论(0)
  • 2021-01-07 16:52

    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
    
    0 讨论(0)
提交回复
热议问题