Print complete key path for all the values of a python nested dictionary
Question:
If below is my nested dictionary I want to parse through recursively and print all the values along with the complete path of the nested key.
my_dict = {'attr':{'types':{'tag':{'name':'Tom', 'gender':'male'},'category':'employee'}}}
Expected output:
Key structure : my_dict["attr"]["types"]["tag"]["name"]<br>
value : "Tom"<br>
Key structure : my_dict["attr"]["types"]["tag"]["gender"]<br>
value : "male"<br>
Key structure : my_dict["attr"]["types"]["category"]<br>
value : "employee"<br>
I wrote a recursive function, but running to this:
my_dict = {'attr':{'types':{'tag':{'name':'Tom','gender':'male'},'category':'employee'}}}
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,dict):
path=path+"_"+k
dict_path(path,v)
else:
path=path+"_"+k
print path,"=>",v
return
dict_path("",my_dict)
Output:
_attr_types_category => employee
_attr_types_category_tag_gender => male
_attr_types_category_tag_gender_name => Tom
In the above : For male, the key struct shouldnt contain “category”
How to retain the correct key structure?
Answers:
You shouldn’t alter the path
variable in the dict_path()
function:
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,dict):
dict_path(path+"_"+k,v)
else:
print path+"_"+k,"=>",v
dict_path("",my_dict)
As catavaran mentions, your problem is caused by adding the new path component to the path
variable inside your for
loop. You need to put the new path into the call so it gets passed to the next level of recursion and doesn’t interfere with the path of subsequent items in the for
loop at the current recursion level.
Here’s an alternate solution that uses a recursive generator, rather than printing the results inside the dict_path
function. (FWIW, I used print json.dumps(my_dict, indent=4)
to reformat the dictionary).
my_dict = {
"attr": {
"types": {
"category": "employee",
"tag": {
"gender": "male",
"name": "Tom"
}
}
}
}
def dict_path(my_dict, path=None):
if path is None:
path = []
for k,v in my_dict.iteritems():
newpath = path + [k]
if isinstance(v, dict):
for u in dict_path(v, newpath):
yield u
else:
yield newpath, v
for path, v in dict_path(my_dict):
print '_'.join(path), "=>", v
output
attr_types_category => employee
attr_types_tag_gender => male
attr_types_tag_name => Tom
Just adding to above @catavaran code.
in case if the dict
value is list
, and if list
may have dict
or values itself, then this code could help.
I just modified delimiter as dot.
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,list):
for i, item in enumerate(v):
dict_path( path + "." + k + "." + str(i), item)
elif isinstance(v,dict):
dict_path(path+"."+k,v)
else:
print path+"."+k, "=>", v
Thanks @catavaran
, your code helped me.
Python 3 with f-strings
def dict_path(my_dict, path=None):
seperator = ":"
if path is None:
path = "{"
for k, v in my_dict.items():
if isinstance(v, dict):
dict_path(v, f"{path}{seperator if path != '{' else ''}{k}")
else:
print(f"{path}{seperator if path != '{' else ''}{k}:{v}" + "}")
dict_path(test_dict)
If below is my nested dictionary I want to parse through recursively and print all the values along with the complete path of the nested key.
my_dict = {'attr':{'types':{'tag':{'name':'Tom', 'gender':'male'},'category':'employee'}}}
Expected output:
Key structure : my_dict["attr"]["types"]["tag"]["name"]<br>
value : "Tom"<br>
Key structure : my_dict["attr"]["types"]["tag"]["gender"]<br>
value : "male"<br>
Key structure : my_dict["attr"]["types"]["category"]<br>
value : "employee"<br>
I wrote a recursive function, but running to this:
my_dict = {'attr':{'types':{'tag':{'name':'Tom','gender':'male'},'category':'employee'}}}
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,dict):
path=path+"_"+k
dict_path(path,v)
else:
path=path+"_"+k
print path,"=>",v
return
dict_path("",my_dict)
Output:
_attr_types_category => employee
_attr_types_category_tag_gender => male
_attr_types_category_tag_gender_name => Tom
In the above : For male, the key struct shouldnt contain “category”
How to retain the correct key structure?
You shouldn’t alter the path
variable in the dict_path()
function:
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,dict):
dict_path(path+"_"+k,v)
else:
print path+"_"+k,"=>",v
dict_path("",my_dict)
As catavaran mentions, your problem is caused by adding the new path component to the path
variable inside your for
loop. You need to put the new path into the call so it gets passed to the next level of recursion and doesn’t interfere with the path of subsequent items in the for
loop at the current recursion level.
Here’s an alternate solution that uses a recursive generator, rather than printing the results inside the dict_path
function. (FWIW, I used print json.dumps(my_dict, indent=4)
to reformat the dictionary).
my_dict = {
"attr": {
"types": {
"category": "employee",
"tag": {
"gender": "male",
"name": "Tom"
}
}
}
}
def dict_path(my_dict, path=None):
if path is None:
path = []
for k,v in my_dict.iteritems():
newpath = path + [k]
if isinstance(v, dict):
for u in dict_path(v, newpath):
yield u
else:
yield newpath, v
for path, v in dict_path(my_dict):
print '_'.join(path), "=>", v
output
attr_types_category => employee
attr_types_tag_gender => male
attr_types_tag_name => Tom
Just adding to above @catavaran code.
in case if the dict
value is list
, and if list
may have dict
or values itself, then this code could help.
I just modified delimiter as dot.
def dict_path(path,my_dict):
for k,v in my_dict.iteritems():
if isinstance(v,list):
for i, item in enumerate(v):
dict_path( path + "." + k + "." + str(i), item)
elif isinstance(v,dict):
dict_path(path+"."+k,v)
else:
print path+"."+k, "=>", v
Thanks @catavaran
, your code helped me.
Python 3 with f-strings
def dict_path(my_dict, path=None):
seperator = ":"
if path is None:
path = "{"
for k, v in my_dict.items():
if isinstance(v, dict):
dict_path(v, f"{path}{seperator if path != '{' else ''}{k}")
else:
print(f"{path}{seperator if path != '{' else ''}{k}:{v}" + "}")
dict_path(test_dict)