简体   繁体   中英

One liner to look up nested value from a dictionary python

Suppose I have a dictionary that is nested arbitrarily:

d = {
    11: {
        21: {31: 'a', 32: 'b'},
        22: {31: 'a', 34: 'c'},
    },
    12: {
        1: {2: 3}
    }
}

And a list of keys whose position tells me which nested dictionary to look for every key in:

keys = [11, 21, 31]
# keys = [11, 23, 44]

Is there a simple one liner to do this? I've looked at the questions listed below, and they are similar, but not really what I'm looking for. I have also attempted it myself and came up with this:

from functools import reduce

def lookup(d, key):
    return d.get(key, {}) if d and isinstance(d, dict) else None

def fn(keys, d):
    return reduce(lookup, keys, d)

print(fn(keys, d)) # prints 'a'

The problem with this, is that in case of second list of keys (see commented out keys), it continues looking up nested keys further, even though the higher level key wasn't found, and continuing is pointless. How can I stop reduce as soon as I find a final match or fail (one of the questions listed below addresses it, but I can't really apply it in my use case... or can I?)? Any other ideas? Oh and I want to accomplish this using official python libraries only. So no numpy , pandas etc, but functools , itertools are fine

Python: Convert list to dict keys for multidimensional dict with exception handling

Is there a simple one-liner for accessing each element of a nested dictioanry in Python?

Accessing nested values in nested dictionaries in Python 3.3

Using itertools for recursive function application

Stopping a Reduce() operation mid way. Functional way of doing partial running sum

Finding a key recursively in a dictionary

Thanks!

You can use functools.reduce() :

from functools import reduce # In Python 2, don't import it. (It's a built-in)

print(reduce(dict.get, keys, d))

# 'a'

For the keys you mentioned, it goes like this:

  • call dict.get with d (initial) and the first item of keys ( 11 ) to get d[11]
  • call dict.get with the result (a dictionary) and the next item in keys ( 21 ) to get {...}[21]
  • call dict.get ...
    ...

until keys is "reduced" to the final value ( 'a' )

Edit : As dict.get results in None if there is no such key, there might be undesired results. If you want to have a KeyError , you can use operator.getitem instead.

Here's a solution I came up with that also gives back useful information when given an invalid lookup path, and allows you to dig through arbitrary json, including nested list and dict structures. (Sorry it's not a one-liner).

def get_furthest(s, path):
    '''
    Gets the furthest value along a given key path in a subscriptable structure.

    subscriptable, list -> any
    :param s: the subscriptable structure to examine
    :param path: the lookup path to follow
    :return: a tuple of the value at the furthest valid key, and whether the full path is valid
    '''

    def step_key(acc, key):
        s = acc[0]
        if isinstance(s, str):
            return (s, False)
        try:
            return (s[key], acc[1])
        except LookupError:
            return (s, False)

    return reduce(step_key, path, (s, True))
d = {
    11: {
        21: {
            31: 'a from dict'
        },
    },
}

l = [None] * 50
l[11] = [None] * 50
l[11][21] = [None] * 50
l[11][21][31] = 'a from list'

from functools import reduce

goodkeys = [11, 21, 31]
badkeys = [11, 12, 13]

print("Reducing dictionary (good):", reduce(lambda c,k: c.__getitem__(k), goodkeys, d))
try:
    print("Reducing dictionary (bad):", reduce(lambda c,k: c.__getitem__(k), badkeys, d))
except Exception as ex:
    print(type(ex), ex)

print("Reducing list (good):", reduce(lambda c,k: c.__getitem__(k), goodkeys, l))

try:
    print("Reducing list (bad):", reduce(lambda c,k: c.__getitem__(k), badkeys, l))
except Exception as ex:
    print(type(ex), ex)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM