0

If I have a dictionary that is nested, and I pass in a string like "key1.key2.key3" which would translate to:

myDict["key1"]["key2"]["key3"]

What would be an elegant way to be able to have a method where I could pass on that string and it would translate to that key assignment? Something like myDict.set_nested('key1.key2.key3', someValue)

3

3 Answers 3

4

Using only builtin stuff:

def set(my_dict, key_string, value):
    """Given `foo`, 'key1.key2.key3', 'something', set foo['key1']['key2']['key3'] = 'something'"""

    # Start off pointing at the original dictionary that was passed in.
    here = my_dict

    # Turn the string of key names into a list of strings.
    keys = key_string.split(".")

    # For every key *before* the last one, we concentrate on navigating through the dictionary.
    for key in keys[:-1]:
        # Try to find here[key]. If it doesn't exist, create it with an empty dictionary. Then,
        # update our `here` pointer to refer to the thing we just found (or created).
        here = here.setdefault(key, {})

    # Finally, set the final key to the given value
    here[keys[-1]] = value


myDict = {}
set(myDict, "key1.key2.key3", "some_value")
assert myDict == {"key1": {"key2": {"key3": "some_value"}}}

This traverses myDict one key at a time, ensuring that each sub-key refers to a nested dictionary.

You could also solve this recursively, but then you risk RecursionError exceptions without any real benefit.

Sign up to request clarification or add additional context in comments.

Comments

1

There are a number of existing modules that will already do this, or something very much like it. For example, the jmespath module will resolve jmespath expressions, so given:

>>> mydict={'key1': {'key2': {'key3': 'value'}}}

You can run:

>>> import jmespath
>>> jmespath.search('key1.key2.key3', mydict)
'value'

The jsonpointer module does something similar, although it likes / for a separator instead of ..

Given the number of pre-existing modules I would avoid trying to write your own code to do this.

2 Comments

How can I do the opposite of search, and set nested dictionary path to a value?
is there no way to do this with dot notation?
0

EDIT: OP's clarification makes it clear that this answer isn't what he's looking for. I'm leaving it up here for people who find it by title.


I implemented a class that did this a while back... it should serve your purposes. I achieved this by overriding the default getattr/setattr functions for an object.

Check it out! AndroxxTraxxon/cfgutils

This lets you do some code like the following...

from cfgutils import obj

a = obj({
    "b": 123,
    "c": "apple",
    "d": {
        "e": "nested dictionary value"
    }
})

print(a.d.e)
>>> nested dictionary value

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.