-2

Given list1 = [1,2,2,3], list2 = [1,2], what's the simplest way to subtract all element of list2 from list1 to get list list3 = [2,3]

It seems sum work well for two lists but subtraction doesn't.

To clarify: Order doesn't matter. L2 is a subset of L1. Duplicates need to be kept. Therefore can't use set.

>>> [1,2,2,3]+[1,2,3]
[1, 2, 2, 3, 1, 2, 3]

>>> [1,2,2,3]-[1,2]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for -: 'list' and 'list'
martineau
  • 119,623
  • 25
  • 170
  • 301
So Pia
  • 71
  • 1
  • 8
  • What would a negative list element mean? `[] - [1]`? – Patrick Haugh Dec 26 '18 at 15:11
  • 6
    This doesn't make sense. `+` _concatenates_ the lists – roganjosh Dec 26 '18 at 15:11
  • How could you add or subtract two lists of dissimilar length? If the length of both the lists would be same, then you might convert the lists to numpy array & perform addition, subtraction or whatever operation you like. – Aditya Mishra Dec 26 '18 at 15:15
  • 2
    @timgeb I'm not sure that's a good dupe. The answers there all seem to rely on `set` and don't preserve the multiplicity of the lists. If anything, this might be `list((Counter(l1) - Counter(l2)).elements())`, but that won't preserve order. – Patrick Haugh Dec 26 '18 at 15:15
  • 1
    I think you can use `remove` in this situation. The `set` doesn't work here. Try this instead: `[list1.remove(i) for i in list2]` `list1` – Scott Boston Dec 26 '18 at 15:15
  • 1
    OP doesnt want to get the difference between 2 lists, this doesn't appear to be a dup – d_kennetz Dec 26 '18 at 15:16
  • @scottboston that won't work because it removes both instances of `2` – roganjosh Dec 26 '18 at 15:17
  • 1
    @roganjosh `remove` removes the first instance of a value. Per docstring "L.remove(value) -> None -- remove first occurrence of value." – Scott Boston Dec 26 '18 at 15:17
  • But, at best, it seems like an exploit of the OP's data rather than an approach that can be extrapolated. Why should concat add to the end but subtract take from the start, @scottboston – roganjosh Dec 26 '18 at 15:19
  • Sorry folks, given all these discussion, i still don't get what's the simplest way to get l3, if l2 is subset of l1. L.remove()is the closet one i could think of, but how can I move more than one element at a time? (if not using iteration) L.remove([lst]) doesn't work – So Pia Dec 26 '18 at 15:24
  • @SoPia You haven't defined what you want the operation to do very well. Does order matter? Which side of the list are items being removed from? What happens if the right hand list contains more of some item than the left hand list? – Patrick Haugh Dec 26 '18 at 15:27
  • Order doesn't matter. L2 is a subset of L1. Duplications needs to be kept – So Pia Dec 26 '18 at 15:28

3 Answers3

2

You can try using remove:

list1 = [1,2,2,2,3]
list2 = [1,2,2]

[list1.remove(i) for i in list2] 
list1

Output:

[2, 3]

Update without list comprehension, using standard for loops.

for i in list2:
    list1.remove(i)
list1

Output:

[2, 3]
Scott Boston
  • 147,308
  • 15
  • 139
  • 187
  • A follow up question not so relevant to the original problem. Why directly run "list.remove(i) for i in list2" in terminal without the [ ] generate me an error? >>> list1 = [1,2,2,2,3] >>> list2 = [1,2,2] >>> [list1.remove(i) for i in list2] [None, None, None] >>> list1 [2, 3] >>> list1 = [1,2,2,2,3] >>> list.remove(i) for i in list2 File "", line 1 list.remove(i) for i in list2 ^ SyntaxError: invalid syntax – So Pia Dec 26 '18 at 15:36
  • 1
    Oh... you the [] denotes list comprehension which is a way of doing a loop. To re-write that not using list comprehension will be as follows: `for i in list2:` `list1.remove(i)` – Scott Boston Dec 26 '18 at 15:39
  • in python shell, i input: list1.remove(i) for i in list2, without the [], it returns a syntax error. just curious why – So Pia Dec 26 '18 at 15:40
  • @SoPia Yes, without the [] it is not a valid python command, you need to re-write it as I have updated in this answer. – Scott Boston Dec 26 '18 at 15:41
  • 1
    [Here](https://www.pythonforbeginners.com/basics/list-comprehensions-in-python), this can help you! List comprehension can be tricky with beginners :) – Pedro Martins de Souza Dec 26 '18 at 15:42
  • 2
    The OP wants a new list returned, `list3`—modifying `list1` instead isn't the quite same thing and may be unacceptable. – martineau Dec 26 '18 at 16:22
  • Then you could `list3 = list1.copy()` and use the same syntax on list3. – Scott Boston Dec 26 '18 at 16:24
1

You could use collections.Counter and a list comprehension:

from collections import Counter

list1 = [1, 2, 2, 2, 3]
list2 = [1, 2, 2]

counts = Counter(list2)
result = [l for l in list1 if counts.get(l, 0) == 0 or counts.subtract((l,))]

print(result)

Output

[2, 3]

The list comprehension is equivalent to:

result = []
for l in list1:
    if counts.get(l, 0) == 0 or counts.subtract((l,)):
        result.append(l)

The tricky part here is the statement counts.get(l, 0) == 0 or counts.subtract((l,)). The counts.subtract((l,)) means subtract 1 from the count of l and the return value of the expression is None, the fact that None is a boolean-like value (that evals to False) allows to use it a single or expression. So the above or will only be True when counts.get(l, 0) == 0.

Dani Mesejo
  • 61,499
  • 6
  • 49
  • 76
1

This is how I would do it:

def remove_elems(l1, l2):
    removals = set(l2)
    result = []
    for elem in l1:
        if elem in removals:
            removals.remove(elem)
        else:
            result.append(elem)
    return result

l1 = [1,2,2,3]
l2 = [1,2]

print(remove_elems(l1, l2))  # -> [2, 3]
martineau
  • 119,623
  • 25
  • 170
  • 301