GatesOfDelirium
GatesOfDelirium

Reputation: 85

Get a list of all keys in nested dictionary

I want to get a list of all keys in a nested dictionary that contains lists and dictionaries.

I currently have this code, but it seems to be missing adding some keys to the list and also duplicate adds some keys.

keys_list = []
def get_keys(d_or_l, keys_list):
    if isinstance(d_or_l, dict):
        for k, v in iter(sorted(d_or_l.iteritems())):
            if isinstance(v, list):
                get_keys(v, keys_list)
            elif isinstance(v, dict):
                get_keys(v, keys_list)
            else:
                keys_list.append(k)
    elif isinstance(d_or_l, list):
        for i in d_or_l:
            if isinstance(i, list):
                get_keys(i, keys_list)
            elif isinstance(i, dict):
                get_keys(i, keys_list)
    else:
        print "** Skipping item of type: {}".format(type(d_or_l))
    return keys_list

This just takes an empty list and populates it with the keys. d_or_l is a variable and takes the original dict to compare it against.

Upvotes: 5

Views: 17470

Answers (5)

gustavz
gustavz

Reputation: 3160

I would extend @pm007 answer by a python 2 & 3 friendly version:

def get_keys(dl, keys=None):
    keys = keys or []
    if isinstance(dl, dict):
        keys += dl.keys()
        _ = [get_keys(x, keys) for x in dl.values()]
    elif isinstance(dl, list):
        _ = [get_keys(x, keys) for x in dl]
    return list(set(keys))

d = {1: 2, 3: 4, 5: {7: {1: 1}}}
get_keys(d)

Furthermore this feels more convenient as you get a function that returns the desired keys instead of magically altering a list

Upvotes: 2

Olshansky
Olshansky

Reputation: 6404

Here is a simple solution:

def get_nested_keys(d, keys):
    for k, v in d.items():
        if isinstance(v, dict):
            get_nested_keys(v, keys)
        else:
            keys.append(k)

keys_list = []
get_nested_keys(test_listing, keys_list)
print(keys_list)

If you want to know the hierarchy of the keys as well, you can modify the function like so:

def get_nested_keys(d, keys, prefix):
    for k, v in d.items():
        if isinstance(v, dict):
            get_nested_keys(v, keys, f'{prefix}:{k}')
        else:
            keys.append(f'{prefix}:{k}')

Upvotes: 1

Alcognito
Alcognito

Reputation: 41

Updating @MackM's response to Python 3 as dict.iteritems has been deprecated (and I prefer to use f-strings over the .format{} styling):

keys_list = []
def get_keys(d_or_l, keys_list):
    if isinstance(d_or_l, dict):
        for k, v in iter(sorted(d_or_l.items())):  #  Altered line to update deprecated method
            if isinstance(v, list):
                get_keys(v, keys_list)
            elif isinstance(v, dict):
                get_keys(v, keys_list)
            keys_list.append(k)   
    elif isinstance(d_or_l, list):
        for i in d_or_l:
            if isinstance(i, list):
                get_keys(i, keys_list)
            elif isinstance(i, dict):
                get_keys(i, keys_list)
    else:
        print(f'** Skipping item of type: {type(d_or_l)}')  #  Altered line to use f-strings
    return keys_list


unique_keys = list(set(get_keys(my_json_dict, keys_list)))  # Added line as example use case

Upvotes: 0

MackM
MackM

Reputation: 3022

As it stands, your code ignores keys that lead to list or dict values. Remove the else block in your first for loop, you want to add the key no matter what the value is.

keys_list = []
def get_keys(d_or_l, keys_list):
    if isinstance(d_or_l, dict):
        for k, v in iter(sorted(d_or_l.iteritems())):
            if isinstance(v, list):
                get_keys(v, keys_list)
            elif isinstance(v, dict):
                get_keys(v, keys_list)
            keys_list.append(k)   #  Altered line
    elif isinstance(d_or_l, list):
        for i in d_or_l:
            if isinstance(i, list):
                get_keys(i, keys_list)
            elif isinstance(i, dict):
                get_keys(i, keys_list)
    else:
        print "** Skipping item of type: {}".format(type(d_or_l))
    return keys_list

get_keys({1: 2, 3: 4, 5: [{7: {9: 1}}]}, keys_list) returns [1, 3, 9, 7, 5]

To avoid duplication, you could use a set datatype instead of a list.

Upvotes: 5

pm007
pm007

Reputation: 383

This should do the job:

def get_keys(dl, keys_list):
    if isinstance(dl, dict):
        keys_list += dl.keys()
        map(lambda x: get_keys(x, keys_list), dl.values())
    elif isinstance(dl, list):
        map(lambda x: get_keys(x, keys_list), dl)

To avoid duplicates you can use set, e.g.:

keys_list = list( set( keys_list ) )

Example test case:

keys_list = []
d = {1: 2, 3: 4, 5: [{7: {9: 1}}]}
get_keys(d, keys_list)
print keys_list
>>>> [1, 3, 5, 7, 9]

Upvotes: 7

Related Questions