user11322408
user11322408

Reputation:

Compare two dict values keeping lowest value

I am trying to write a function that takes in two dicts and returns a merged dict which containing all keys from both dicts and, if any keys exist in both dicts, comparing prices to keep the lowest value.

x = "{'45': 450, '43': 500, '44.5': 420, '39': 415, '47': 320, '46': 520, '44': 400, '47.5': 480, '40.5': 407, '42.5': 407, '42': 401, '38': 401, '45.5': 435, '37.5': 415, '41': 506, '38.5': 787, '36': 399, '36.5': 410, '48.5': 380, '40': 406, '48': 287, '49.5': 567, '50.5': 850, '51.5': 399, '49': 386}"
y = "{'36': 345.0, '36.5': 360.0, '37.5': 355.0, '38': 375.0, '38.5': 375.0, '39': 370.0, '40': 380.0, '40.5': 395.0, '41': 345.0, '42': 300.0, '42.5': 230.0, '43': 220.0, '44': 220.0, '44.5': 220.0, '45': 220.0, '45.5': 290.0, '46': 225.0, '47': 300.0, '47.5': 265.0, '48': 425.0, '48.5': 275.0, '49': 2000.0, '49.5': 1350.0, '51.5': 2000.0}"

I wrote this function to do it, but I believe it can be written in a more pythonic way

import ast
def compare_prices(dict1,dict2):
    temp1 = ast.literal_eval(dict1)
    temp2 = ast.literal_eval(dict2)
    for k,v in temp1.items():
        if k in temp2.keys():
            price = temp2[k] if temp2[k] else False
            if price:
                if v> price:
                    temp1[k] = price
                else:
                    temp1[k] = v
            else:
                temp1[k] = v
        else:
            temp1[k] = v
    for k,v in temp2.items():
        if k not in temp1.keys():
            try:
                temp1[k] = v if v else ''
            except TypeError:
                temp1[k] = v
    return dict(sorted(temp1.items()))

Upvotes: 1

Views: 542

Answers (3)

Avishka Dambawinna
Avishka Dambawinna

Reputation: 1215

You can use **kwargs to merge the Dictionaries because it expands the contents in a dictionary as a collection of key-value pairs. And we can do the comparison to the keys that have multiple values in the resultant dictionary,

import ast    

def compare_prices(dict1, dict2):
    temp1 = ast.literal_eval(dict1)
    temp2 = ast.literal_eval(dict2)
    tempDict = {**temp1, **temp2}   # two or more Dicts merged using **kwargs
    for key, value in tempDict.items():
        if key in temp1 and key in temp2:
            tempDict[key] = min([value, temp1[key]])    # sets the minimum value

    return tempDict 

Output,

print(compare_prices(x, y))
#{'45': 220.0, '43': 220.0, '44.5': 220.0, '39': 370.0, '47': 300.0, '46': 225.0, '44': 220.0, '47.5': 265.0, '40.5': 395.0, '42.5': 230.0, '42': 300.0, '38': 375.0, '45.5': 290.0, '37.5': 355.0, '41': 345.0, '38.5': 375.0, '36': 345.0, '36.5': 360.0, '48.5': 275.0, '40': 380.0, '48': 287, '49.5': 567, '50.5': 850, '51.5': 399, '49': 386}

If you want a sorted dictionary you can use OrderedDict from collectionslibrary

import ast
from collections import OrderedDict

def compare_prices(dict1, dict2):
    temp1 = ast.literal_eval(dict1)
    temp2 = ast.literal_eval(dict2)
    tempDict = {**temp1, **temp2}   # two or more Dicts merged using **kwargs
    for key, value in tempDict.items():
        if key in temp1 and key in temp2:
            tempDict[key] = min([value, temp1[key]])    # sets the minimum value

    return OrderedDict(sorted(tempDict.items(), key=lambda t: t[0]))

Output,

print(compare_prices(x, y))
#OrderedDict([('36', 345.0), ('36.5', 360.0), ('37.5', 355.0), ('38', 375.0), ('38.5', 375.0), ('39', 370.0), ('40', 380.0), ('40.5', 395.0), ('41', 345.0), ('42', 300.0), ('42.5', 230.0), ('43', 220.0), ('44', 220.0), ('44.5', 220.0), ('45', 220.0), ('45.5', 290.0), ('46', 225.0), ('47', 300.0), ('47.5', 265.0), ('48', 287), ('48.5', 275.0), ('49', 386), ('49.5', 567), ('50.5', 850), ('51.5', 399)])

Upvotes: 0

Alain T.
Alain T.

Reputation: 42133

You could do it with a dictionary comprehension by merging the two dictionary with the minimum value of common keys:

{ **x, **y, **{k:min(x[k],y[k]) for k in x if k in y} }

output:

{'45': 220.0, '43': 220.0, '44.5': 220.0, '39': 370.0, '47': 300.0, '46': 225.0, '44': 220.0, '47.5': 265.0, '40.5': 395.0, '42.5': 230.0, '42': 300.0, '38': 375.0, '45.5': 290.0, '37.5': 355.0, '41': 345.0, '38.5': 375.0, '36': 345.0, '36.5': 360.0, '48.5': 275.0, '40': 380.0, '48': 287, '49.5': 567, '50.5': 850, '51.5': 399, '49': 386}

or you could write it like this (to avoid a double merge when most keys are common):

{ **x, **{ k:v for k,v in y.items() if k not in x or x[k]<v} }

or, you could use a descending sort on the concatenation of (key,value) tuples but sorting the whole data just to get minimums between matching keys is going to be very inefficient:

dict(sorted((*x.items(),*y.items()),key=lambda i:-i[1]))

If you are not comfortable with dictionary comprehensions, you could use a simple for loop:

merged = x.copy()
for k,v in y.items(): merged[k] = min(v, merged.get(k,v))

Upvotes: 1

David Buck
David Buck

Reputation: 3844

I believe this will meet your requirements. It's much simpler as it uses sets of keys so that set1.intersection(set2) contains all of the keys in both sets, so it can use this to make the comparisons, and set2-set1 just contains those unique to set2, so they can simply be moved to set1

def compare_prices(dict1,dict2):
    temp1 = ast.literal_eval(dict1)
    temp2 = ast.literal_eval(dict2)
    set1 = set(temp1.keys())
    set2 = set(temp2.keys())
    for k in set1.intersection(set2):
        if temp1[k] > temp2[k]:
            temp1[k] = temp2[k]
    for k in set2 - set1:
        temp1[k] = temp2[k]
    return dict(sorted(temp1.items()))

Output is:

print(compare_prices(x, y))
{'36': 345.0, '36.5': 360.0, '37.5': 355.0, '38': 375.0, '38.5': 375.0, '39': 370.0, '40': 380.0, '40.5': 395.0, '41': 345.0, '42': 300.0, '42.5': 230.0, '43': 220.0, '44': 220.0, '44.5': 220.0, '45': 220.0, '45.5': 290.0, '46': 225.0, '47': 300.0, '47.5': 265.0, '48': 287, '48.5': 275.0, '49': 386, '49.5': 567, '50.5': 850, '51.5': 399}

Upvotes: 1

Related Questions