Checking a nested dictionary using a dot notation string "a.b.c.d.e", automatically create missing levels

Solution 1:

You could use an infinite, nested defaultdict:

>>> from collections import defaultdict
>>> infinitedict = lambda: defaultdict(infinitedict)
>>> d = infinitedict()
>>> d['key1']['key2']['key3']['key4']['key5'] = 'test'
>>> d['key1']['key2']['key3']['key4']['key5']
'test'

Given your dotted string, here's what you can do:

>>> import operator
>>> keys = "a.b.c".split(".")
>>> lastplace = reduce(operator.getitem, keys[:-1], d)
>>> lastplace.has_key(keys[-1])
False

You can set a value:

>>> lastplace[keys[-1]] = "something"
>>> reduce(operator.getitem, keys, d)
'something'
>>> d['a']['b']['c']
'something'

Solution 2:

... or using recursion:

def put(d, keys, item):
    if "." in keys:
        key, rest = keys.split(".", 1)
        if key not in d:
            d[key] = {}
        put(d[key], rest, item)
    else:
        d[keys] = item

def get(d, keys):
    if "." in keys:
        key, rest = keys.split(".", 1)
        return get(d[key], rest)
    else:
        return d[keys]

Solution 3:

How about an iterative approach?

def create_keys(d, keys):
    for k in keys.split("."):
        if not k in d: d[k] = {}  #if the key isn't there yet add it to d
        d = d[k]                  #go one level down and repeat

If you need the last key value to map to anything else than a dictionary you could pass the value as an additional argument and set this after the loop:

def create_keys(d, keys, value):
    keys = keys.split(".")
    for k in keys[:-1]:
        if not k in d: d[k] = {}
        d = d[k]            
    d[keys[-1]] = value