JustAGuy
JustAGuy

Reputation: 5941

Finding a key inside a nested dictionary with Python

Ditionary:

data = {
    "president": {
        "name": "Zaphod Beeblebrox",
        "species": "Betelgeusian"
    }
}

What I'd like to do is return if a key named "name" exists.

if 'president' in data and 'name' in data['president']:
    print("found")

This works fine but only if I have pre knowledge of the dictionary's structure. Is there a way of knowing if there's a subkey called "name" recursively? I only found how to do this for the top level.

Upvotes: 1

Views: 98

Answers (3)

pault
pault

Reputation: 43494

A bit of a hacky non-traditional way would be to convert your dict to a string using json.dumps. Then you can search for the key as in the string.

For example:

import json 

def find_key(d, key):
    if not isinstance(d, dict):
        return False
    else:
        return '"'+key+'": ' in json.dumps(d)

print(find_key(data, 'name'))
#True

One limitation is that this won't work if you can have "name: " inside one of your values.

Upvotes: 0

jpp
jpp

Reputation: 164623

You can define a recursive generator with try / except:

def recursive_keys(d):
    for k, v in d.items():
        try:
            yield from recursive_keys(v)
        except AttributeError:
            yield k

'name' in set(recursive_keys(data))  # True

If you prefer, you can use isinstance instead:

def recursive_keys(d):
    for k, v in d.items():
        if isinstance(v, dict):
            yield from recursive_keys(v)
        else:
            yield k

This is relatively expensive, since you are first creating a set of all keys before checking if an item exists in the set. As per @pault's comment, you can use:

any(x == 'name' for x in recursive_keys(data))

For a lazy solution without generators, see @DanielMesejo's solution. This may be more efficient since it avoids expensive __next__ calls.

Upvotes: 1

Dani Mesejo
Dani Mesejo

Reputation: 61910

You can do a recursive search:

data = {
    "president": {
        "name": "Zaphod Beeblebrox",
        "species": "Betelgeusian"
    }
}

def r_find(target, key):
    if key in target:
        return True
    else:
        for k, v in target.items():
            if isinstance(v, dict):
                if r_find(v, key):
                    return True
        return False


print(r_find(data, "name"))
print(r_find(data, "species"))
print(r_find(data, "no-name"))

Output

True
True
False

Upvotes: 2

Related Questions