我想将字典的嵌套列表转换为子结构。并找到一种稳健的方法来做到这一点
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"
},
]
进入:
{
"vehicle": {
"car": {
"sedan" : {}
}
},
"fruit" : {
"apple": {},
"grape": {},
"pineapple": {}
}
}
注意,在这种情况下,它可以下降两级。但它也可以深入三层。例如,一个附加条目:
{
"id" : "sedan",
"name": "mini sedan"
}
到目前为止,我的方法是:
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
在这种情况下是有效的。问题是它不够健壮。我在想递归,但无法破解。有人能帮忙吗?谢谢
解决方案
您可以使用collections.defaultdict
和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)
输出:
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
是可变的 working_dict
是所有"id"
的参考表- 如果没有这样的id,则为其注册
{}
- 并将没有
id
字段的元素作为根元素注册到result_dict
中
附加
如果不想使用collections.defaultdict
,则只能使用dict.setdefault
。但它更为冗长。
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)
您也可以使用递归函数手动执行此操作。想法:
- 在输入列表中的元素上迭代
- 忽略没有
id
键的元素 - 对于键中具有
id
的元素:- 在out中递归搜索此键
- 添加元素(如果元素已经存在,则在递归函数中添加该元素,否则在后面添加(
# 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': {}
# }
# }
# }