我从Web API检索了一系列dicts。每个字典都有name
,description
,'父'和children
键。 children
键在其值时具有一系列dicts。为了清楚起见,这是一个虚拟的例子:
[
{'name': 'top_parent', 'description': None, 'parent': None,
'children': [{'name': 'child_one'},
{'name': 'child_two'}]},
{'name': 'child_one', 'description': None, 'parent': 'top_parent',
'children': []},
{'name': 'child_two', 'description': None, 'parent': 'top_parent',
'children': [{'name': 'grand_child'}]},
{'name': 'grand_child', 'description': None, 'parent': 'child_two',
'children': []}
]
数组中的每个项目。项可能是最顶层的父项,因此不存在于任何children
数组中。项目可以是孩子也可以是父母。或者一个项目只能是一个孩子(没有自己的孩子)。
所以,在树形结构中,你有这样的东西:
top_parent
child_one
child_two
grand_child
在这个人为设计的简化例子中,top_parent
是父母而非孩子; child_one
是孩子,但不是父母; child_two
是父母和孩子; grand_child
是孩子,但不是父母。这涵盖了所有可能的状态。
我想要的是能够迭代dicts数组1次并生成一个正确表示树结构的嵌套dict(但是,1次是不可能的,最有效的方式)。所以,在这个例子中,我会得到一个看起来像这样的字典:
{
'top_parent': {
'child_one': {},
'child_two': {
'grand_child': {}
}
}
}
严格地说,没有孩子的项目没有必要成为关键,但这是更可取的。
答案 0 :(得分:2)
第四次编辑,显示三个版本,清理了一下。第一个版本自上而下工作,并按照您的要求返回None,但实质上是循环通过顶级数组3次。下一个版本只循环一次,但返回空的dicts而不是None。
最终版本自下而上,非常干净。它可以使用单个循环返回空的dicts,或者使用其他循环返回None:
from collections import defaultdict
my_array = [
{'name': 'top_parent', 'description': None, 'parent': None,
'children': [{'name': 'child_one'},
{'name': 'child_two'}]},
{'name': 'child_one', 'description': None, 'parent': 'top_parent',
'children': []},
{'name': 'child_two', 'description': None, 'parent': 'top_parent',
'children': [{'name': 'grand_child'}]},
{'name': 'grand_child', 'description': None, 'parent': 'child_two',
'children': []}
]
def build_nest_None(my_array):
childmap = [(d['name'], set(x['name'] for x in d['children']) or None)
for d in my_array]
all_dicts = dict((name, kids and {}) for (name, kids) in childmap)
results = all_dicts.copy()
for (name, kids) in ((x, y) for x, y in childmap if y is not None):
all_dicts[name].update((kid, results.pop(kid)) for kid in kids)
return results
def build_nest_empty(my_array):
all_children = set()
all_dicts = defaultdict(dict)
for d in my_array:
children = set(x['name'] for x in d['children'])
all_dicts[d['name']].update((x, all_dicts[x]) for x in children)
all_children.update(children)
top_name, = set(all_dicts) - all_children
return {top_name: all_dicts[top_name]}
def build_bottom_up(my_array, use_None=False):
all_dicts = defaultdict(dict)
for d in my_array:
name = d['name']
all_dicts[d['parent']][name] = all_dicts[name]
if use_None:
for d in all_dicts.values():
for x, y in d.items():
if not y:
d[x] = None
return all_dicts[None]
print(build_nest_None(my_array))
print(build_nest_empty(my_array))
print(build_bottom_up(my_array, True))
print(build_bottom_up(my_array))
结果:
{'top_parent': {'child_one': None, 'child_two': {'grand_child': None}}}
{'top_parent': {'child_one': {}, 'child_two': {'grand_child': {}}}}
{'top_parent': {'child_one': None, 'child_two': {'grand_child': None}}}
{'top_parent': {'child_one': {}, 'child_two': {'grand_child': {}}}}
答案 1 :(得分:1)
您可以保持从名称到节点的延迟映射,然后通过仅处理parent
链接来重建层次结构(我假设数据是正确的,因此如果A
被标记为父级在B
的子女中列出B
iff A
。
nmap = {}
for n in nodes:
name = n["name"]
parent = n["parent"]
try:
# Was this node built before?
me = nmap[name]
except KeyError:
# No... create it now
if n["children"]:
nmap[name] = me = {}
else:
me = None
if parent:
try:
nmap[parent][name] = me
except KeyError:
# My parent will follow later
nmap[parent] = {name: me}
else:
root = me
输入的children
属性仅用于知道元素是否应该作为None
存储在其父级中(因为没有子级),或者它是否应该是字典,因为它将在重建过程结束时生孩子。将没有子节点的节点存储为空字典可以通过避免需要这种特殊情况来简化代码。
使用collections.defaultdict
代码也可以简化以创建新节点
import collections
nmap = collections.defaultdict(dict)
for n in nodes:
name = n["name"]
parent = n["parent"]
me = nmap[name]
if parent:
nmap[parent][name] = me
else:
root = me
此算法为O(N)
,假定为常量字典访问,并且只对输入进行一次传递,并且需要O(N)
空间用于name->节点映射(空间要求为{{1} }对于原始O(Nc)
版本,其中nochildren->None
是包含子项的节点数。)
答案 2 :(得分:0)
我刺伤了它:
persons = [\
{'name': 'top_parent', 'description': None, 'parent': None,\
'children': [{'name': 'child_one'},\
{'name': 'child_two'}]},\
{'name': 'grand_child', 'description': None, 'parent': 'child_two',\
'children': []},\
{'name': 'child_two', 'description': None, 'parent': 'top_parent',\
'children': [{'name': 'grand_child'}]},\
{'name': 'child_one', 'description': None, 'parent': 'top_parent',\
'children': []},\
]
def findParent(name,parent,tree,found = False):
if tree == {}:
return False
if parent in tree:
tree[parent][name] = {}
return True
else:
for p in tree:
found = findParent(name,parent,tree[p],False) or found
return found
tree = {}
outOfOrder = []
for person in persons:
if person['parent'] == None:
tree[person['name']] = {}
else:
if not findParent(person['name'],person['parent'],tree):
outOfOrder.append(person)
for person in outOfOrder:
if not findParent(person['name'],person['parent'],tree):
print 'parent of ' + person['name'] + ' not found
print tree
结果:
{'top_parent': {'child_two': {'grand_child': {}}, 'child_one': {}}}
它还会接收尚未添加其父级的所有子级,然后在最后对其进行协调。