给定下面的JSON,为给定的"id"创建"name"的分层列表的最佳方法是什么?层次结构中可以有任意数量的节。
例如,提供id"156"将返回"添加存储设备,引导配置,配置"
我一直在考虑使用iteritems()
,但需要一些帮助。
{
"result": true,
"sections": [
{
"depth": 0,
"display_order": 1,
"id": 154,
"name": "Configuration",
"parent_id": null,
"suite_id": 5
},
{
"depth": 1,
"display_order": 2,
"id": 155,
"name": "Guided Configuration",
"parent_id": 154,
"suite_id": 5
},
{
"depth": 2,
"display_order": 3,
"id": 156,
"name": "Add Storage Devices",
"parent_id": 155,
"suite_id": 5
},
{
"depth": 0,
"display_order": 4,
"id": 160,
"name": "NEW",
"parent_id": null,
"suite_id": 5
},
{
"depth": 1,
"display_order": 5,
"id": 161,
"name": "NEWS",
"parent_id": 160,
"suite_id": 5
}
]
}
这里有一种方法:
def get_path(data, section_id):
path = []
while section_id is not None:
section = next(s for s in data["sections"] if s["id"] == section_id)
path.append(section["name"])
section_id = section["parent_id"]
return ", ".join(path)
它假设data
是json.loads(json_text)
或类似的结果,而section_id
是int
(这就是您在该示例JSON中获得的ID)。
对于您的示例用法:
>>> get_path(data, 156)
u'Add Storage Devices, Guided Configuration, Configuration'
可能最简单的方法是创建一个将ID映射到名称的字典。例如:
name_by_id = {}
data = json.loads(the_json_string)
for section in data['sections']:
name_by_id[section['id']] = section['name']
或使用dict理解:
name_by_id = {section['id']: section['name'] for section in data['sections']}
然后你可以得到特定的元素:
>>> name_by_id[156]
... 'Add Storage Devices'
或获取所有ID:
>>> name_by_id.keys()
... [160, 161, 154, 155, 156]
这可能就是您想要的:
>>> sections = data['sections']
>>> lookup = {section['id']: section for section in sections}
>>> lookup[None] = {}
>>> for section in sections:
parent = lookup[section['parent_id']]
if 'childs' not in parent:
parent['childs'] = []
parent['childs'].append(section)
>>> def printRecurse (section, indent = 0):
if 'childs' in section:
section['childs'].sort(lambda x: x['display_order'])
for child in section['childs']:
print('{}{}: {}'.format(' ' * indent, child['id'], child['name']))
printRecurse(child, indent + 1)
>>> printRecurse(lookup[None])
154: Configuration
155: Guided Configuration
156: Add Storage Devices
160: NEW
161: NEWS
我相信你想要这样的东西:
def get_name_for_id(id_num, sections):
cur_depth = -1
texts = []
for elem in sections:
if elem['depth'] < cur_depth:
del texts[:]
elif elem['depth'] == cur_depth:
texts.pop()
texts.append(elem['name'])
cur_depth = elem['depth']
if elem['id'] == id_num:
return ', '.join(reversed(texts))
使用您的数据返回:
In [11]: get_name_for_id(156, data['sections'])
Out[11]: 'Add Storage Devices, Guided Configuration, Configuration'
此外,它还考虑了基于depth
的层次结构,因此,如果在您的数据中id
156指的是depth = 0
,则结果为:
In [16]: get_name_for_id(156, data['sections'])
Out[16]: 'Add Storage Devices'
如果id 156的深度为1,则返回的值为:
In [22]: get_name_for_id(156, data['sections'])
Out[22]: 'Add Storage Devices, Configuration'
基本上它考虑了树:
深度156=0深度156=1深度156=2
154 156 154 154
| | |
| / 155
155 155 156 |
156
并且它返回从156到树的根的路径中的名称的串联。
您也可以这样做。但为此,您的输入应该是这样的。将输入字典中的null
替换为None
,将true
替换为True
。
def filtering(d,id_n):
names = []
while id_n:
id_n,name=[(sec['parent_id'],sec['name']) for sec in d['sections'] if sec['id'] == id_n][0]
names.append(name)
return names
d = {
"result": True, #making 'true' with 'True'
"sections": [
{
"depth": 0,
"display_order": 1,
"id": 154,
"name": "Configuration",
"parent_id": None,
"suite_id": 5
},
{
"depth": 1,
"display_order": 2,
"id": 155,
"name": "Guided Configuration",
"parent_id": 154,
"suite_id": 5
},
{
"depth": 2,
"display_order": 3,
"id": 156,
"name": "Add Storage Devices",
"parent_id": 155,
"suite_id": 5
},
{
"depth": 0,
"display_order": 4,
"id": 160,
"name": "NEW",
"parent_id": None,
"suite_id": 5
},
{
"depth": 1,
"display_order": 5,
"id": 161,
"name": "NEWS",
"parent_id": 160,
"suite_id": 5
}
]
}
用给定的输入测试代码:-
id_n = 156
>>> filtering(d,id_n)
['Add Storage Devices', 'Guided Configuration', 'Configuration']