-1

I'm trying to make a code that will subtract a list from a list.

For example:

list1 = ['hi', 'hi', 'once', 'twice']
list2 = ['hi', 'once', 'twice']

The result from the subtraction of the lists that I want is:

differenceoflists = ['hi']

Currently, I'm using the code

list1 = ['hi', 'hi', 'once', 'twice']
list2 = ['hi', 'once', 'twice']
differenceoflists = []
for i in list1:
    if i not in list2:
        differenceoflists.append(i)
print(differenceoflists)

However, this code doesn't function the way I want it to. It returns an empty list when I want it to return ['hi']. How can I do this correctly?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
qafka
  • 47
  • 5
  • But `'hi'` is in `list2`, so it cannot be in `differenceoflists`. Why do you expect it to be? – mkrieger1 Feb 24 '21 at 22:02
  • https://stackoverflow.com/questions/3462143/get-difference-between-two-lists – kol Feb 24 '21 at 22:02
  • Well, there's two hi's in list1, and there's only one in list2, so I'd like the code to count the extra hi in list1. – qafka Feb 24 '21 at 22:04
  • It's returning an empty list because the same '`hi'` in `list2` is being matched twice. Depending on your performance and output constraints, you might be able to use your idea except work off of a *copy* of list2, and remove things from list2 using `.remove(item)` as you add them to the `differenceoflists`. – Hanry Hu Feb 24 '21 at 22:04

2 Answers2

4

This can also be done using collections.Counter you can subtract Counters from each-other then just turn the result into a list:

from collections import Counter

list1 = ['hi', 'hi', 'once', 'twice']
list2 = ['hi', 'once', 'twice']

difference_of_lists = list((Counter(list1) - Counter(list2)).elements())

print(difference_of_lists)

['hi']
Jab
  • 26,853
  • 21
  • 75
  • 114
  • Instead of reinventing the wheel with a generator expression you can call the [`elements`](https://docs.python.org/3/library/collections.html#collections.Counter.elements) method of a `Counter` object to convert it to a list. – blhsing Feb 24 '21 at 22:25
1

Your code does not work correctly because it does not keep track of how often an item from list2 has been used to discard a corresponding item from list1.

Instead of building the differenceoflists by adding items, you can create it by starting with a copy of list1 and removing all items from list2. That way, corresponding pairs of items are matched exactly once, and if an item is contained more often in list1 than it is in list2, the extras will remain.

To remove an item from a list you can use the remove method. You need to handle the case when the item you try to remove isn't in the list (by just ignoring it).

def differenceoflists(list1, list2):
    d = list1.copy()
    for i in list2:
        try:
            d.remove(i)
        except ValueError:
            pass  # ignore that i is not in list1
    return d

There are some subtle differences in the order in which the items are contained in the result when comparing this code with the code shown in Jab's answer. Depending on your exact needs you need to choose one or the other.

differenceoflists removes items from the left, while differenceoflists_jab removes items from the right:

>>> differenceoflists([1, 3, 5, 1], [5, 1])
[3, 1]
>>> differenceoflists([1, 3, 5, 1], [1, 5])
[3, 1]
>>> differenceoflists_jab([1, 3, 5, 1], [5, 1])
[1, 3]
>>> differenceoflists_jab([1, 3, 5, 1], [1, 5])
[1, 3]

differenceoflists_jab does not preserve the order of the items in list1:

>>> differenceoflists([1, 3, 5, 1], [5])
[1, 3, 1]
>>> differenceoflists_jab([1, 3, 5, 1], [5])
[1, 1, 3]
mkrieger1
  • 19,194
  • 5
  • 54
  • 65