TheMethod
TheMethod

Reputation: 3001

Dynamic dict value access with dot separated string

I'm using Python 3.5.1

So what I am trying to do is pass in a dict a dot separated string representing the path to a key and a default value. I want to check for the keys existence and if it's not there , provide the default value. The problem with this is that the key I want to access could be nested in other dicts and I will not know until run time. So what I want to do is something like this:

def replace_key(the_dict, dict_key, default_value):
    if dict_key not in the_dict:
       the_dict[dict_key] = default_value
    return the_dict

some_dict = {'top_property': {'first_nested': {'second_nested': 'the value'}}}
key_to_replace = 'top_property.first_nested.second_nested'
default_value = 'replaced'
#this would return as {'top_property': {'first_nested': {'second_nested': 'replaced'}}}
replace_key(some_dict, key_to_replace, default_value) 

What I'm looking for is a way to do this without having to do a split on '.' in the string and iterating over the possible keys as this could get messy. I would rather not have to use a third party library. I feel like there is clean built in Pythonic way to do this but I just can't find it. I've dug through the docs but to no avail. If anyone has any suggestion as to how I could do this it would be very much appreciated. Thanks!

Upvotes: 1

Views: 1174

Answers (2)

Stefano G.
Stefano G.

Reputation: 309

the easyest way that I've found to do this, namely get value using a "key path" by "dotted string" is using replace and eval:

for key in pfields:
    if key.find('.') > 0:
        key = key.replace(".", "']['")
    try:
        data = str(eval(f"row['{key}']"))
    except KeyError:
        data = ''

And this is an example of the keys:

lfields = ['cpeid','metadata.LinkAccount','metadata.DeviceType','metadata.SoftwareVersion','mode_props.vfo.CR07.VIKPresence','mode_props.vfo.CR13.VIBHardVersion']

With this raw solution You don't need install other library

Upvotes: -2

Munchhausen
Munchhausen

Reputation: 450

You could use recursivity:

def replace_key(the_dict, dict_keys, default_value):
    if dict_keys[0] in the_dict:
        if len(dict_keys)==1:
            the_dict[dict_keys[0]]=default_value
        else:
            replace_key(the_dict[dict_keys[0]], dict_keys[1:],default_value)
    else:
        raise Exception("wrong key")


some_dict = {'top_property': {'first_nested': {'second_nested': 'the value'}}}
key_to_replace = 'top_property.first_nested.second_nested'
default_value = 'replaced'
#this would return as {'top_property': {'first_nested': {'second_nested': 'replaced'}}}
replace_key(some_dict, key_to_replace.split("."), default_value)

But it still uses the split(). But maybe you consider it to be less messy?

Upvotes: 3

Related Questions