Creating a dictionary from two lists but keeping the minimum values

Viewed 126

I'm working with a dictionary, and I'm having a problem, so let's say I have two lists that I need to create a dict, I know, I can use zip to do this, but the thing is, the list I'm using for keys ( list1 ) have similar tuples. This is just an example, the lists can have more elements, but the len in both lists are always the same.

list1 = [(3, 2), (2, 4), (2, 4), (4, 3), (4, 2), (4, 3)]

list2 = [68, 34, 29, 29, 3, 59]

mydict = dict(zip(list1, list2))

print(mydict)

Output:

{(3, 2): 68, (2, 4): 29, (4, 3): 59, (4, 2): 3}

I know that the keys are unique, and if two keys are the same, the associated value will be the last one to appear, but what I need it's that if two keys are the same, then, the associated value needs to be the minimum. For example, in list1 I have two tuples repeated (2,4) with associated values of 34 and 29, so when the dictionary is created I need to keep the 29, and the other duplicated tuple is (4,3) with values of 29 and 59, and if the dictionary is created I want the value to be 29, not 59. my desired output:

{(3, 2): 68, (2, 4): 29, (4, 3): 29, (4, 2): 3}

There is some way I can achieve this? Any help will be appreciated, thank you!

5 Answers

Starting with

keys = [(3, 2), (2, 4), (2, 4), (4, 3), (4, 2), (4, 3)]
values = [68, 34, 29, 29, 3, 59]

Make key-value pairs that are sorted by value, descending, then build the dict from that:

>>> dict(sorted(zip(keys, values), key=lambda x: -x[1]))
{(3, 2): 68, (4, 3): 29, (2, 4): 29, (4, 2): 3}

It's the same idea as your existing dict(zip()) combination but re-ordering the values before building the dictionary, such that entries with lower values appear later - therefore, the minimum value for the key is last to appear.

one way is to sort the entries so that minimum value is last in the list:

def get_value(pair): return pair[1]

list1 = [(3, 2), (2, 4), (2, 4), (4, 3), (4, 2), (4, 3)]

list2 = [68, 34, 29, 29, 3, 59]

mydict = dict(sorted(zip(list1, list2), key=get_value, reverse=True))

Rather than trying to do a complicated, convoluted one liner or comprehension, a simple for loop will perform fine and maintain readability. My suggestion would be something like this

def merge_lists(list1, list2):
    mydict = {}
    for k, v in zip(list1,list2):
        if (k not in mydict) or (mydict[k] > v):
            mydict[k] = v
    return mydict

list1 = [(3, 2), (2, 4), (2, 4), (4, 3), (4, 2), (4, 3)]

list2 = [68, 34, 29, 29, 3, 59]

mydict = merge_lists(list1, list2)

print(mydict)

# mydict = {(3, 2): 68, (2, 4): 29, (4, 3): 29, (4, 2): 3}

If you want to play with it more, some further ideas would be to sort list 2 and move the indices of list1 in tandem. Then, with that structure, if you get a repeated element in list1 you know it must it must have come second. You wouldn't really get any perfomance boost or other benefit, but it is an alternative.

One solution is to fill mydict step-by-step:

list1 = [(3, 2), (2, 4), (2, 4), (4, 3), (4, 2), (4, 3)]

list2 = [68, 34, 29, 29, 3, 59]

mydict = {}
for k,v in zip(list1,list2):
    if k in mydict:
        mydict[k] = min(mydict[k],v)
    else:
        mydict[k] = v

print(mydict)

There must be IMO a one-liner solution if desired :-)

from operator import itergetter
from itertools import groupby

pairs = sorted(zip(list1, list2))
minima = {
    key: min(value for _, value in group)
    for key, group in groupby(sorted(pairs), itemgetter(0))
}
Related