Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Programmatically accessing arbitrarily deeply-nested values in a dictionary [duplicate]

I'm working on a Python script where I'm given lists of strings of the format: ['key1', 'key2', 'key2.key21.key211', 'key2.key22', 'key3'].

Each value in the list corresponds to an entry in a dictionary, and for entries structured like 'key2.key21.key211', they correspond to (in this example), a key 'key211' nested within 'key21', itself nested inside 'key2'.

The above list corresponds to the dictionary:

x = {
     'key1' : 'value1',
     'key2' : {
               'key21' : {
                          'key211': 'value211'
                         },
               'key22' : 'value22'
              },
     'key3' : 'value3'
    }

The names are not necessarily as regular as key(n)+; they can be of the form food.vegetables.potato, for example. The only guarantees I have is that the key names themselves, in the dictionary, do not contain the . character, and that the dictionary definitely contains all the entries referenced in the original list.

My question is, given such a list of strings, how do I programmatically access the corresponding entries in a dictionary? I can think of a solution using eval(), as well as one using just a traversal/search, but I want to avoid calls to eval(), and I get the impression that a traversal with comparisons would be slow (since dicts aren't search trees), and would entail a lot of nasty exception handling.

like image 899
Jules Avatar asked Jun 04 '15 15:06

Jules


1 Answers

One approach is to write a function to access keys in nested dicts.

def deep_access(x,keylist):
     val = x
     for key in keylist:
         val = val[key]
     return val

s = 'key2.key21.key211'

print deep_access(x,s.split('.'))

result:

value211

Another approach, if you want to use similar syntax as a normal dictionary access you could subclass dict and override __getitem__ to allow for nested access when a tuple of keys is provided:

class NestedDict(dict):

    def __getitem__(self,keytuple):
        # if key is not a tuple then access as normal
        if not isinstance(keytuple, tuple):
            return super(NestedDict,self).__getitem__(keytuple)
        d = self
        for key in keytuple:
            d = d[key]
        return d

>>> nd = NestedDict(x)
>>> nd['key2']
{'key22': 'value22', 'key21': {'key211': 'value211'}}
>>> nd['key2','key22']
'value22'
>>> nd['key2','key21']
{'key211': 'value211'}
>>> nd['key2','key21','key211']
'value211'

You can then similarly implement __setitem__ and __delitem__ as needed.

like image 122
Eric Appelt Avatar answered Oct 12 '22 00:10

Eric Appelt