1

I have two lists, and I'd like to subtract one from the other.

I have seen people using sets, but my lists contain duplicates, so I believe this is not an option for me.

int_list = [(1,1), (-1, 210), (-1, 210)]
new_list = [(-1, 210)]

final_list = [item for item in int_list if item not in new_list]

I get final_list = [(1,1)], but I would like to keep the second copy of (-1,210) in the final list. I understand why this is happening, but I do not know another way to subtract the lists. I guess I can return the final list as it is, then append any elements from int_list which appear more than once, and which also appear in the new_list, but I feel like there should be an easier way. any help appreciated

jonan
  • 217
  • 1
  • 8
  • 1
    so if you wanted to remove both, would you set `new_list = [(-1, 210), (-1, 210)]`? Or are you only asking to remove ONE instance (the first instance?) of the values in `new_list`? – pault Jul 31 '19 at 18:42
  • 2
    can the second list also contain duplicates? – slider Jul 31 '19 at 18:43
  • if the second list was `new_list = [(-1,210), (-1,210)]`, then I would want neither of the copies to appear in the final list. – jonan Jul 31 '19 at 18:48

3 Answers3

2

There is a hack-ey way of doing this that also preserves ordering:

int_list = [(1,1), (-1, 210), (-1, 210)]
new_list = [(-1, 210)]

final_list = [item for item in int_list if item not in new_list or new_list.remove(item)]
# final_list == [(1, 1), (-1, 210)]

This makes sure we don't filter too many items from int_list. It should also work if new_list contains duplicates as well. Note that it mutates new_list, so make a copy beforehand if needed.

iz_
  • 15,923
  • 3
  • 25
  • 40
  • It's generally not recommended to rely on [side effects within a list comprehension](https://stackoverflow.com/questions/5753597/is-it-pythonic-to-use-list-comprehensions-for-just-side-effects). You could easily do the same in a simple loop. On the other hand, I suppose one could argue that this is not purely for the side effect. – pault Jul 31 '19 at 18:46
  • 1
    @pault I agree that a loop might be more clear, but I don't think the list comprehension is unwarranted. I am constructing a new list (it's not purely for side effects). – iz_ Jul 31 '19 at 18:47
2

Simple:

for x in new_list:
    if x in int_list: int_list.remove(x)

Note : remove removes the first matching value

Saurav Sahu
  • 13,038
  • 6
  • 64
  • 79
0

You can create a Counter for the first list and subtract that with a Counter of the second. Then you can recreate the list with the remaining counts:

from collections import Counter
from itertools import chain

int_list = [(1,1), (-1, 210), (-1, 210)]
new_list = [(-1, 210)]

final_list_counts = Counter(int_list) - Counter(new_list)
final_list = list(chain(*[[t] * count for t, count in final_list_counts.items()]))
print(final_list)  # [(1, 1), (-1, 210)]
slider
  • 12,810
  • 1
  • 26
  • 42