Search code examples
pythondefaultdict

Return value of largest key if key not found in defaultdict


Requirement

I would like to define a defaultdict that returns the value of the largest key, if the key that I am providing is not in the dictionary. Basically I am looking for a way to store config information with the twist, that values default to their last defined values.

Solution so far

My implementation is as follows:

from collections import defaultdict

d = defaultdict(lambda: d[max(d.keys())])
d.update({2010: 10, 2011: 20, 2013: 30 })

for year in [2010, 2011, 2013, 2014]:
    print(f"{year}: {d[year]}")

which correctly produces:

2010: 10
2011: 20
2013: 30
2014: 30

(a more elaborate version could also return values for keys smaller than the smallest).

Question

Is there a more elegant way to define the lambda function without the requirement that you know the dictionary's name?


Solution

  • Calculating frequently the max key looks quite inefficient because in Python keys are in hash maps so they aren't sorted.

    Consider writing your own default_dict:

    class DictLast(collections.MutableMapping,dict):
        def __init__(self, *args, **kwargs):
            self.theMaxKey = None 
            self.update(*args, **kwargs)
        def __getitem__(self, key):
            if dict.__contains__(self,key): 
                return dict.__getitem__(self,key)
            return dict.__getitem__(self, self.theMaxKey)
        def __setitem__(self, key, value):
            if self.theMaxKey is None:
                self.theMaxKey = key
            if key > self.theMaxKey: 
                self.theMaxKey = key
            dict.__setitem__(self,key,value)   
    
    d = DictLast()
    d.update({2010: 10, 2011: 20, 2013: 30 })
    
    for year in [2010, 2011, 2013, 2014]:
        print(f"{year}: {d[year]}")
    

    please notice that, whenever asked for a missing key, my implementation doesn't add the key to the dictionary. I assume this is the intended behavior

    If you are in doubt, try this code with my and your implementation:

    for year in [2010, 2011, 2013, 2015]:
        print(f"{year}: {d[year]}")
    d[2014]=7
    for year in [2010, 2011, 2013, 2015]:
        print(f"{year}: {d[year]}")
    

    to understand what's the difference

    EDIT: As pointed out in the comments below:

    "this strategy only works if you don't remove items from the dictionary".

    IMO If you want the data structure to be well designed you should manage removal too (up to you to decide if you want to raise, recalculate the max or do something different).