我基本上是尝试将平面json文件转换为树视图。这里,树视图所需的父子关系由使用source和target的链接键进行。
以下是示例原始输入:
{
"nodes" : [
{
name: "bz_db",
index: 0
},
{
name: "mysql",
index: 1
},
{
name: "postgres",
index: 2
},
{
name: "it-infra",
index: 3
},
{
name: "user-count",
index: 4
}
],
links: [
{
source: 0, target: 1
},
{
source: 0, target: 3
},
{
source: 1, target: 3
},
{
source: 3, target: 4
}
]
}
正如您所看到的,链接字段维护了这种关系,最后我想要这种格式的数据:
{
name: "bz_db",
children: [
{
name: "mysql",
children: [
{
name: "it-infra",
children: [
{
name: "user_count",
children: []
}
]
}
]
},
{
name: "it-infra",
children: [{
name: "user_count",
children: []
}
]
}
]
}
我试图解决这个问题,但它适用于1级(显示所选根元素的直接子级。
var findObjectByKeyValue = function(arrayOfObject, key, value){
return _.find(arrayOfObject, function(o){ return o[key] == value})
}
var rootObject = findObjectByKeyValue(sample_raw_input.nodes, 'name', 'bz_db');
var treeObject = {
name: rootObject.name,
index: rootObject.index,
root: true,
children: []
};
angular.forEach(dependencyData.links, function(eachLink){
if(treeObject.index == eachLink.source){
var rawChildObject = findObjectByKeyValue(dependencyData.nodes, 'index', eachLink.target);
var childObject = {};
childObject.index = rawChildObject.index;
childObject.name = rawChildObject.name;
childObject.children = [];
treeObject.children.push(childObject);
}
});
但是上面的代码只返回了第一级的depndencies,但我想要层次关系。 我知道我可以在这里使用递归。但我对它不太满意。
答案 0 :(得分:4)
Josh的回答使用了一系列map
- > filter
- > map
- > find
个调用,每个调用都通过收集数据。随着集合中节点数量的增加,这个循环循环循环会导致惊人的计算复杂度。
通过在每个reduce
和nodes
上使用单个links
传递,您可以大大简化树的创建。与需要线性时间(较慢)的数组Map
相比,find
也可以在对数时间内执行查找。当您考虑为输入的每个元素调用此操作时,可以清楚地看到时间上的显着差异。
const makeTree = (nodes = [], links = []) =>
links.reduce
( (t, l) =>
t.set ( l.source
, MutableNode.push ( t.get (l.source)
, t.get (l.target)
)
)
, nodes.reduce
( (t, n) => t.set (n.index, MutableNode (n.name))
, new Map
)
)
.get (0)
最后,我们提供了我们依赖的MutableNode
界面
const MutableNode = (name, children = []) =>
({ name, children })
MutableNode.push = (node, child) =>
(node.children.push (child), node)
以下是完整的程序演示。 JSON.stringify
仅用于显示结果
const MutableNode = (name, children = []) =>
({ name, children })
MutableNode.push = (node, child) =>
(node.children.push (child), node)
const makeTree = (nodes = [], links = []) =>
links.reduce
( (t, l) =>
t.set ( l.source
, MutableNode.push ( t.get (l.source)
, t.get (l.target)
)
)
, nodes.reduce
( (t, n) => t.set (n.index, MutableNode (n.name))
, new Map
)
)
.get (0)
const data =
{ nodes:
[ { name: "bz_db", index: 0 }
, { name: "mysql", index: 1 }
, { name: "postgres", index: 2 }
, { name: "it-infra", index: 3 }
, { name: "user-count", index: 4 }
]
, links:
[ { source: 0, target: 1 }
, { source: 0, target: 3 }
, { source: 1, target: 3 }
, { source: 3, target: 4 }
]
}
const tree =
makeTree (data.nodes, data.links)
console.log (JSON.stringify (tree, null, 2))

答案 1 :(得分:1)
您可以依赖跟踪对象引用,并且无需任何递归即可执行此操作。使用Object.assign
,将节点列表映射到其子节点:
// Assuming that input is in `input`
const nodes = input.nodes.reduce((a, node) => {
a[node.index] = { ...node, index: undefined };
return a;
}, []);
// organize the links by their source
const links = input.links.reduce((a, link) => {
return a.set((a.get(link.source) || []).concat(nodes[link.target]);
}, new Map());
// Apply side effect of updating node children
nodes.forEach(node => Object.assign(node, {
children: links.get(node.index),
}));
因此,我将获取节点列表,并为每个节点分配(以改变节点本身 - 请记住这是一个副作用)一个新数组。那些children
是链接此节点的所有链接,我们Array#map
将它们target
ID转换为我们想要的实际节点。
答案 2 :(得分:0)
只分享样本,与您的样本略有不同。 但这给了您递归功能的提示。
jsFiddle flat array json transform to recursive tree json
function getNestedChildren(arr, parent) {
var out = []
for(var i in arr) {
if(arr[i].parent == parent) {
var children = getNestedChildren(arr, arr[i].id)
if(children.length) {
arr[i].children = children
}
out.push(arr[i])
}
}
return out
}
var flat = [
{id: 1, title: 'hello', parent: 0},
{id: 2, title: 'hello', parent: 0},
{id: 3, title: 'hello', parent: 1},
{id: 4, title: 'hello', parent: 3},
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4},
{id: 7, title: 'hello', parent: 3},
{id: 8, title: 'hello', parent: 2}
]
var nested = getNestedChildren(flat, 0)
console.log(nested)