remove duplicate value from list of tuples based on values from another list

Viewed 1388

I have 2 lists similar to these:

l1 = [('zero', 0),('one', 2),('two', 3),('three', 3),('four', 5)]
l2 = [('zero', 0),('one', 3),('four', 2),('ten', 3),('twelve', 8)]

I want to compare the lists and

  1. remove duplicates from both if both values are the same
  2. if the first value is a match remove the tuple from the list where the second value is lower

I can do the first with

l3 = [(a,b) for (a,b) in l1 if (a,b) not in l2]
l4 = [(a,b) for (a,b) in l2 if (a,b) not in l1]

or using set though it doesn't preserve order

l3 = set(l1) - set(l2)

but I'm having a hard time figuring out the second. I tried to start with removing based on just the first value with

l3 = [(a,b) for (a,b) in l1 if a not in l2]

but that doesn't work. My desired output for l3 & l4 is:

l3
[('two', 3),('three', 3),('four', 5)]

l4
[('one', 3),('ten', 3),('twelve', 8)]

Any guidance would be appreciated.

3 Answers

You could do:

d1 = dict(l1)
d2 = dict(l2)

l3 = [(k, v) for k, v in d1.items() if k not in d2 or d2[k] < v]
l4 = [(k, v) for k, v in d2.items() if k not in d1 or d1[k] < v]

print(l3)
print(l4)

Output

[('two', 3), ('three', 3), ('four', 5)]
[('one', 3), ('ten', 3), ('twelve', 8)]

The idea is to use dictionaries for fast lookups of matching first values, if any, and then check if the corresponding second value is less to the one in the current list.

You can put both lists in a dict, compare the values under the keys and construct your output lists:

l1 = [('zero', 0),('one', 2),('two', 3),('three', 3),('four', 5)]
l2 = [('zero', 0),('one', 3),('four', 2),('ten', 3),('twelve', 8)]

# using dicts as lookups needs some performance to build them but then is faster then
# pure list lookups
d1 = dict(l1)
d2 = dict(l2)

# collect things to delete in sets
del1 = set()
del2 = set()

for key in set(d1.keys()).union(d2):
    if key in d1 and key in d2:
        if d1[key] == d2[key]:
            del1.add((key, d1[key]))
            del2.add((key, d1[key]))
            continue

        elif d2[key] >= d1[key]:
            del1.add((key, d1[key]))

        else:
            del2.add((key, d2[key]))

# create the resulting lists
l3 = [i for i in l1 if i not in del1]
l4 = [i for i in l2 if i not in del2]

print(l3) 
print(l4)

Output:

[('two', 3), ('three', 3), ('four', 5)]
[('one', 3), ('ten', 3), ('twelve', 8)]

l5 = list(set([i for i in l3 for y in l4 if (i[0]==y[0] and i[1] > y[1]) or (i[0] not in [x[0] for x in l4]) ]))
l6 = list(set([i for i in l4 for y in l3 if (i[0]==y[0] and i[1] > y[1]) or (i[0] not in [x[0] for x in l3]) ]))

print(l5)
print(l6)

>>> [('three', 3), ('four', 5), ('two', 3)]
>>> [('ten', 3), ('twelve', 8), ('one', 3)]

Explanation:

For an element to be added to the new list the either condition:

(i[0]==y[0] and i[1] > y[1]) or (i[0] not in [x[0] for x in l4]) must be true,

i is the element in l3, y is the element in l4 this condition is that firstly if the first values match and the second value for i is greater than the second value for y then add it to list which fulfils your part of the question

if the first value is a match remove the tuple from the list where the second value is lower

then the second part of the condition is just adding elements which are not in the other list.

I applied this same logic for list l6

Related