collections.Counter
is invaluable for things like this - like a set but with multiplicity. The algorithm is simple - we make a Counter
for each list, and then for the opposite list we just check whether each element is in the other list's counter. If so, remove one occurrence and put it in the result_list
. Otherwise, put it in the not_equals_list
.
from collections import Counter
list_1 = [1, 5, 2, 3, 2, 4, 1, 3]
list_2 = [1, 2, 2, 3, 1, 6]
counter_1 = Counter(list_1)
counter_2 = Counter(list_2)
result_list_1 = []
result_list_2 = []
not_equals_list_1 = []
not_equals_list_2 = []
for item in list_1:
if counter_2[item] > 0:
result_list_1.append(item)
counter_2[item] -= 1
else:
not_equals_list_1.append(item)
for item in list_2:
if counter_1[item] > 0:
result_list_2.append(item)
counter_1[item] -= 1
else:
not_equals_list_2.append(item)
print(result_list_1) # [1, 2, 3, 2, 1]
print(result_list_2) # [1, 2, 2, 3, 1]
print(not_equals_list_1) # [5, 4, 3]
print(not_equals_list_2) # [6]
Order is preserved in not_equals_list
from the order of the first list. If you desire it differently, you can use reversed()
where necessary to change either order of iteration or to simply flip the result.
If using this type of solution with custom objects, you'll need to make sure that __hash__()
is properly implemented for equality checking - since both sets and dicts are hashtable-based, and Counter
is just a subclass of dict
.
From a quick google search, multiset
might provide a more efficient way of doing this by just converting your lists into sets and then doing set operations on them. I haven't tested this, though.