Merging ranked lists of tuples based on common id

I have the following sorted lists of tuples:

list1 = [(0.2, 'a'), (0.4, 'b'), (0.5,'d')]
list2 = [(0.1, 'a'), (0.3, 'c'), (0.7, 'x')]
list3 = [(0.5, 'c'), (0.6, 'a'), (0.5, 'b')]

I want to create an overall ranked list based on the common letters as follows:

  1. If the letter is common in all three lists, add the three individual values
  2. If the letter is only common between two lists, add the two individual values and a 1
  3. If the element is only in one list, add 2 to its value

Expected result:

[(0.9, 'a'), (1.8, 'c'), (1.9, 'b'), (2.5, 'd'), (2.7, 'x')]

What is working:

I am able to get the expected result if the item is common in all three lists but I am unable to get correct results if it’s the other cases.

Code snippet

list1 = [(0.2, 'a'), (0.4, 'b'), (0.5, 'd')]
list2 = [(0.1, 'a'), (0.3, 'c'), (0.7, 'x')]
list3 = [(0.5, 'c'), (0.6, 'a'), (0.5, 'b')]
priority_result = [] # when element is common in all 3 lists
twos_array = [] #when element is common in only two lists

result = [(s1, l1 + l1) for (l1, s1), (l1, s2) in zip(list1, list2)]
for (score, resultID) in list1:
    for (score1, resultID1) in list2:
        for (score2, resultID2) in list3:
            if(resultID == resultID1 or resultID == resultID2):                    
                result = [(score + score1 + score2, resultID)]
            elif(resultID == resultID1 and resultID != resultID2):
                result = [(score + score1 + 1, resultID)]

How can I work on this to produce the desired outcome?


You can swap the order of the tuples to create a mapping:

d1 = dict(x[::-1] for x in list1)
d2 = dict(x[::-1] for x in list2)
d3 = dict(x[::-1] for x in list3)

Now you can make a union of the keys, since dict.keys returns a set-like object:

keys = d1.keys() | d2.keys() | d3.keys()

The rest can be done with dict.get:

result = {k: d1.get(k, 1) + d2.get(k, 1) + d3.get(k, 1) for k in keys}

Turning this into a sorted list is straightforward:

sorted(x[::-1] for x in result.items())

Let’s say your lists were in a meta-list now:

lists = [list1, list2, list3]
keys = set().union(*lists)
dicts = [dict(x[::-1] for x in l) for l in lists]
result = {k: sum(d.get(k, 1) for d in dicts) for k in keys}
result = sorted(x[::-1] for x in result.items())

Here is a slightly simpler solution:

mapping = dict.fromkeys(set().union(*lists), len(lists))
for v, k in itertools.chain.from_iterable(lists):
    mapping[k] += v - 1
result = sorted(x[::-1] for x in result.items())

You can use collections.Counter to do most of the math for you:

c = Counter()
for lst in lists:
    c.update({k: v - 1 for v, k in lst})
result = [(v + len(lists), k) for k, v in c.items()]

The same thing with a regular collections.defaultdict is:

d = defaultdict(int)
for v, k in itertools.chain.from_iterable(lists):
    d[k] += v - 1
result = [(v + len(lists), k) for k, v in d.items()]