get dictionary key by path (string)

Question:

I have this path that can change from time to time:

'#/path/to/key'

The parts of the path aren’t defined, so this value is also fine

'#/this/is/a/longer/path'

I’m splitting this key at ‘/’ so I get

['#', 'path', 'to', 'key']

and I need to get to the key in this path, let’s say my dict is exp, so I need to get to here:

exp['path']['to']['key']

how could I possibly know how to get to this key?

Asked By: Elon Salfati

||

Answers:

>>> exp = {'path': {'to': {'key': 42}}}
>>> my_key = exp
>>> for i in '#/path/to/key'.split('/')[1:]:
>>>     my_key = my_key[i]
>>> print(my_key)
42

But I’m a bit curious about how you retrieved such dict

Answered By: gogaz

Assuming what you mean by this is that your array ['#', 'path', 'to', 'key'] has indexes leading into a nested starting from index 1, you could iterate over each item in the list starting from the second and just dig deeper through every iteration.

For example, in Python 3 you could do this.

def get_key_from_path(exp, path):
    """Returns the value at the key from <path> in <exp>.
    """
    cur = exp
    for dir in path[1:]:
        cur = exp[dir]
    return cur
Answered By: Ziyad Edher
def get_key_by_path(dict_obj, path_string):
    path_list = path_string.split('/')[1:]
    obj_ptr = dict_obj
    for elem in path_list:
        obj_ptr = obj_ptr[elem]
    return obj_ptr
Answered By: Tai

Use the recursion, Luke …

def deref_multi(data, keys):
    return deref_multi(data[keys[0]], keys[1:]) 
        if keys else data

last = deref_multi(exp, ['path','to','key'])

UPDATE: It’s It’s been 5+ years, time for an update, this time without using recursion (which may use slightly more resources than if Python does the looping internally). Use whichever is more understandable (and so maintainable) to you:

from functools import reduce
def deref_multi(data, keys):
    return reduce(lambda d, key: d[key], keys, data)
Answered By: BareNakedCoder

There have been some good answers here, but none of them account for paths that aren’t correct or paths that at some point result in something that is not subscriptable. The code below will potentially allow you a little more leeway in handling such cases whereas other code so far will just throw an error or have unexpected behavior.

path = '#/path/to/key'
exp = {'path' : { 'to' : { 'key' : "Hello World"}}}

def getFromPath(dictionary, path):
    curr = dictionary
    path = path.split("/")[1:] # Gets rid of '#' as it's uneccessary 
    while(len(path)):
        key = path.pop(0)
        curr = curr.get(key)
        if (type(curr) is not dict and len(path)):
            print("Path does not exist!")
            return None 
    return curr

print(getFromPath(exp, path)) #Your value
Answered By: Khauri

I suggest you to use python-benedict, a python dict subclass with full keypath support and many utility methods.

You just need to cast your existing dict:

exp = benedict(exp)
# now your keys can be dotted keypaths too
exp['path.to.key']

Here the library and the documentation:
https://github.com/fabiocaccamo/python-benedict

Note: I am the author of this project

Answered By: Fabio Caccamo

Using functools in place of recursion:

# Define:
from functools import partial, reduce
deref = partial(reduce, lambda d, k: d[k])

# Use:
exp = {'path': {'to': {'key': 42}}}
deref(('path', 'to', 'key'), exp)

3 year old question, I know… I just really like functools.

Answered By: user7048748
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.