Reputation: 85
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
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
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
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
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
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