Search code examples
listpython-2.7nestedautovivification

Update a python dictionary with an arbitrarily long list of values


I have a work problem in which I needed to be able to update values in a dictionary from an arbitrarily long list of keys. Both the key list and the dictionary are generated from the same data at run time, but I don't know how many keys will be in the data before. The list of keys to take from the data is user specified at run time.

OK, so this works out to having to be able to update values in a dictionary from a list information consisting of: 1. a list of keys, ordered in the same order as the key nesting in the dictionary 2. a value to update per that key list information.


Solution

  • I think I've got a solution, by scavenging from this site: https://gist.github.com/hrldcpr/2012250 , and also the python autoviv(), which i liked better than that default tree object.

    Here's my solution. Depending on what you feed the function, you should be able to generate a dictionary from list(s), and/or update a value at a specific place in the structure.

    If you spot glaring errors or opportunities for improvement, I would appreciate your constructive feedback.

    from pprint import *
    from collections import defaultdict
    from operator import getitem
    
    class autoviv(dict):
        """Implementation of perl's autovivification feature."""
        def __getitem__(self, item):
            try:
                return dict.__getitem__(self, item)
            except KeyError:
                value = self[item] = type(self)()
                return value
    
    def add(t,path,genorupdate='generate',val=0):
        if genorupdate=='generate':
            for node in path:
                t = t[node]         
        elif genorupdate=='update':
            for node in path:
                if path.index(node)==len(path)-1:
                    t[node]=val     
                t = t[node] 
    
    d=autoviv()
    
    # test lists to generate dictionary
    
    l=['a','b','c']
    l2=['a','b','d']
    
    # TEST 1: generate dictionary with 2 kvps: c and d, nested under b:
    
    add(d,l)
    add(d,l2)
    
    for k in d.keys():
        print k, d[k]
    
    # RESULT: a {'b': {'c': {}, 'd': {}}}
    
    # TEST 2, update the value for the a,b,d key to 2 w/o disturbing anything else or
        # generating additional structure
    
    add(d,l2,'update',2)
    
    for k in d.keys():
        print k, d[k]
    
    # RESULT: a {'b': {'c': {}, 'd': 2}}