Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check key existence in nested dictionaries

Tags:

python

json

I am working with API calls and thus Python dictionaries.

However, for the same request, I don't always key the same keys, and I'd like to know when I can call a key without an Exception...

Let's say I have

test = {'a':{'b':{'c':{'d':'e'}}}}

Sometimes key d will exist, sometimes it won't. Sometimes c won't even exist.

I'd like to, somehow, check if test['a']['b']['c']['d'] exists, in one line.

What I've tried so far:

  • Using test.get('a', {}).get('b', {}).get('c', {}).get('d', {}). Works fine but it's a mess, sometimes I have 5-6 nested dictionaries with really long names...

  • Using try/except block which is nice, but usually if test['a']['b']['c']['d'] does not exist, I will try calling test['a']['b']['e']['f'] to check if that one exists, thus I would need to add a try/catch for every of my if statements, as if I am not wrong, if an exception is catch, try block is not executed anymore.

I was maybe trying to look to a somekind of reflexive way to do it, calling a function with the name of my "object" as a string, that would check if every key exists, and if it does, return the object itself.

Any thoughts?

The usage behind it would be, omitting useless case, and assuming sometimes the info is in test['a']['b']['c']['d'], sometimes in test['a']['b']['f'] :

if test['a']['b']['c']['d'] **exists**:
    do sthg with the value of test['a']['b']['c']['d']
elif test['a']['b']['f'] **exists**:
    do sthg else with the value of test['a']['b']['f']
else:
    do sthg different

If I put a try/except there, won't the first exception stop the execution and don't let me execute the elif?

Moreover, I really like the way of calling test['a']['b']['c']['d'] better than giving a list of keys. In fact, I want it to be as transparent as possible for me and for the people who will read/use my code.

like image 652
user2497262 Avatar asked Jun 05 '15 23:06

user2497262


People also ask

How do you check if a value exists in a nested dictionary?

Use get() and Key to Check if Value Exists in a Dictionary You can use this function as a condition to determine whether or not a value exists within a dictionary.

How do you check for the existence of a key in a dictionary?

You can check if a key exists in a dictionary using the keys() method and IN operator. The keys() method will return a list of keys available in the dictionary and IF , IN statement will check if the passed key is available in the list. If the key exists, it returns True else, it returns False .

How do I get a list of key values in a dictionary?

keys() function to make your code more explicit. To get a list of the dictionary's values, you can call the dict. values() function.

How do you break nested dictionaries in Python?

In Python, we use “ del “ statement to delete elements from nested dictionary.


2 Answers

You could write a recursive function to check:

def f(d, keys):
    if not keys:
        return True
    return keys[0] in d and f(d[keys[0]], keys[1:])

If the function returns True, the keys exist:

In [10]: f(test,"abcd")
Out[10]: True

In [11]: f(test,"abce")
Out[11]: False

If you want to test multiple key combinations:

for keys in ("abce","abcr","abcd"):
    if f(test,keys):
        print(keys)
        break
abcd

To return the value it is pretty simple:

def f(d, keys):
    if len(keys) == 1:
         return d[keys[0]] if keys[0] in d else False
    return keys[0] in d and f(d[keys[0]], keys[1:])

print(f(test,"abcd"))
e

You can test again for multiple key combinations:

def test_keys(keys):
    for keys in keys:
        val = f(test,keys)
        if val:
            return val
    return False


print(test_keys(("abce","abcr","abc")))

You can also write the function iteratively:

def f(d, keys):
    obj = object
    for k in keys:
        d = d.get(k, obj)
        if d is obj:
            return False
    return d

print(f(test,"abcd"))
e

If you want to run a condition based on the return values:

def f(d, keys):
    obj = object
    for k in keys:
        d = d.get(k, obj)
        if d is obj:
            return False
    return d

from operator import mul

my_actions = {"c": mul(2, 2), "d": lambda: mul(3, 3), "e": lambda: mul(3, 3)}

for st in ("abce", "abcd", "abcf"):
    val = f(test, st)
    if val:
        print(my_actions[val]())
9

Just test the key combo in the same order you would with your if/elif's etc..

like image 135
Padraic Cunningham Avatar answered Oct 10 '22 07:10

Padraic Cunningham


It's not exactly what you want because it doesn't check existence, but here's a one-liner similar to the dict.get method:

In [1]: test = {'a':{'b':{'c':{'d':'e'}}}}
In [2]: keys = 'abcd' # or ['a', 'b', 'c', 'd']

In [3]: reduce(lambda d, k: d.get(k) if d else None, keys, test)
Out[3]: 'e'

In [4]: keys = 'abcf'

In [5]: reduce(lambda d, k: d.get(k) if d else None, keys, test)

Unfortunately it's not very efficient because it doesn't stop as soon as one of the keys is missing.

like image 38
Cristian Ciupitu Avatar answered Oct 10 '22 07:10

Cristian Ciupitu