I would like to convert a nested list of dictionary into a substructure. And finding a robust way to do so. The structure:
nested_list = [
{
"id" : "fruit",
"name" : "apple"
},
{
"name": "fruit"
},
{
"id" : "fruit",
"name" : "grape"
},
{
"id" : "fruit",
"name" : "pineapple"
},
{
"name": "vehicle"
},
{
"id" : "vehicle",
"name": "car"
},
{
"id" : "car",
"name": "sedan"
},
]
Into:
{
"vehicle": {
"car": {
"sedan" : {}
}
},
"fruit" : {
"apple": {},
"grape": {},
"pineapple": {}
}
}
Note that in this case it can go two level down. But It can go three deep down as well. For example one additional entry:
{
"id" : "sedan",
"name": "mini sedan"
}
My approach so far is:
for category in nested_list:
if 'id' not in category:
d[category['name']] = {}
for category in nested_list:
if 'id' in category and category['id'] in d:
d[category['id']][category['name']] = {}
elif 'id' in category and category['id'] not in d:
for k, v in d.items():
if category['id'] in v:
d[k][category['id']] = {category['name']: {}}
# If there are not top level key then do nothing
else:
pass
It works in this case. The problem is it's not robust enough. I'm thinking recursion but unable to crack it. Can someone help out? Thank you
You can use collections.defaultdict
and dict.setdefault
:
from collections import defaultdict
nested_list = [
{
"id": "fruit",
"name": "apple"
},
{
"name": "fruit"
},
{
"id": "fruit",
"name": "grape"
},
{
"id": "fruit",
"name": "pineapple"
},
{
"name": "vehicle"
},
{
"id": "vehicle",
"name": "car"
},
{
"id": "car",
"name": "sedan"
},
{
"id": "sedan",
"name": "mini sedan"
},
]
working_dict = defaultdict(dict)
result_dict = {}
for item in nested_list:
name = item['name']
if 'id' in item:
id_ = item['id']
working_dict[id_].setdefault(name, working_dict[name])
else:
result_dict[name] = working_dict[name]
print(working_dict)
print(result_dict)
output:
defaultdict(<class 'dict'>, {'fruit': {'apple': {}, 'grape': {}, 'pineapple': {}}, 'apple': {}, 'grape': {}, 'pineapple': {}, 'vehicle': {'car': {'sedan': {'mini sedan': {}}}}, 'car': {'sedan': {'mini sedan': {}}}, 'sedan': {'mini sedan': {}}, 'mini sedan': {}})
{'fruit': {'apple': {}, 'grape': {}, 'pineapple': {}}, 'vehicle': {'car': {'sedan': {'mini sedan': {}}}}}
dict
is mutable.working_dict
is reference table for all "id"
s. {}
for it.id
field, as root elements into result_dict
.If you don't want to use collections.defaultdict
, you can only use dict.setdefault
. But it is more verbose.
working_dict = {}
result_dict = {}
for item in nested_list:
name = item['name']
if 'id' in item:
id_ = item['id']
working_dict.setdefault(id_, {}).setdefault(name, working_dict.setdefault(name, {}))
else:
result_dict[name] = working_dict.setdefault(name, {})
print(result_dict)
You can also do it manually with a recursive function. The idea:
id
keyid
in keys:
# Recursive search
def iterdictAdd(d, id, name):
# For all element in the dict
for k, v in d.items():
# If key match -> add
if k == id:
d[id] = {**d[id], **{name: {}}}
return True
else:
# Recursive call
if isinstance(v, dict):
if iterdictAdd(v, id, name):
return True
return False
out = {}
# Iterate all dict
for dict_ in nested_list:
# Ignore elements without "id"
if "id" in dict_:
# Search and add this key
if not iterdictAdd(out, dict_["id"], dict_["name"]):
out[dict_["id"]] = {dict_["name"]: {}}
print(out)
# {'fruit': {
# 'apple': {},
# 'grape': {},
# 'pineapple': {}
# },
# 'vehicle': {
# 'car': {
# 'sedan': {}
# }
# }
# }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.