[英]Javascript: Building a hierarchical tree
我的数据具有以下属性:
我第一次尝试构建一棵树如下。 由于递归导致无限循环,因此存在问题。 即使我解决了它,我也不确定是否有更好的方法来做到这一点。 目前,我分两次完成。
我希望它尽可能高效,因为我有大量的数据。 它还需要动态重建树(根可以是任何节点)
下面的程序中有示例数据:
arry = [{"Id":"1", "Name":"abc", "Parent":""}, {"Id":"2", "Name":"abc", "Parent":"1"},
{"Id":"3", "Name":"abc", "Parent":"2"},{"Id":"4", "Name":"abc", "Parent":"2"}]//for testing
我希望输出是(它可能是错误的嵌套结构,因为我手动编写了它。但是,我希望是一个有效的 JSON 结构,其中节点作为字段“值”,子项作为数组。)
{
"value": {"Id":"1", "Name":"abc", "Parent":""},
"children": [
{
"value": {"Id":"2", "Name":"abc", "Parent":"1"},
"children": [
{
"value": {"Id":"3", "Name":"abc", "Parent":"2"},
"children": []
},
{
"value": {"Id":"4", "Name":"abc", "Parent":"2"},
"children": []
}
]
..
}
示例程序:
function convertToHierarchy(arry, root)
{
//root can be treated a special case, as the id is known
arry = [{"Id":"1", "Name":"abc", "Parent":""}, {"Id":"2", "Name":"abc", "Parent":"1"},
{"Id":"3", "Name":"abc", "Parent":"2"},{"Id":"4", "Name":"abc", "Parent":"2"}]//for testing
var mapping = {}; // parent : [children]
for (var i = 0; i < array.length; i++)
{
var node = arry[i];
if (!mapping[node.Id]) {
mapping[node.Id] = {value: node, children:[] } ;
}else{
mapping[node.Id] = {value: node} //children is already set
}
if (!mapping[node.Parent]) { //TODO what if parent doesn't exist.
mapping[node.Parent] = {value: undefined, children:[ {value: node,children:[]} ]};
}else {//parent is already in the list
mapping[node.Parent].children.push({value: node,children:[]} )
}
}
//by now we will have an index with all nodes and their children.
//Now, recursively add children for root element.
var root = mapping[1] //hardcoded for testing, but a function argument
recurse(root, root, mapping)
console.log(root)
//json dump
}
function recurse(root, node, mapping)
{
var nodeChildren = mapping[node.value.Id].children;
root.children.push({value:node.value, children:nodeChildren})
for (var i = 0; i < nodeChildren.length; i++) {
recurse(root, nodeChildren[i], mapping);
}
return root;
}
到目前为止,我有 3 个很好的解决方案,希望投票建议更惯用、更有效的实施。 我不确定,利用我的数据的属性,输入数组集中只有一个根元素,而且根总是给出,这些实现中的任何一个都可能更好。 我还应该学习如何进行基准测试,因为我的要求是重建树的效率(快速/没有太多内存)。 例如,输入已经被缓存(数组)并像这样重建树
convertToHierarchy(parentid)
....
convertToHierarchy(parentid2)
...
这是一种解决方案:
var items = [
{"Id": "1", "Name": "abc", "Parent": "2"},
{"Id": "2", "Name": "abc", "Parent": ""},
{"Id": "3", "Name": "abc", "Parent": "5"},
{"Id": "4", "Name": "abc", "Parent": "2"},
{"Id": "5", "Name": "abc", "Parent": ""},
{"Id": "6", "Name": "abc", "Parent": "2"},
{"Id": "7", "Name": "abc", "Parent": "6"},
{"Id": "8", "Name": "abc", "Parent": "6"}
];
function buildHierarchy(arry) {
var roots = [], children = {};
// find the top level nodes and hash the children based on parent
for (var i = 0, len = arry.length; i < len; ++i) {
var item = arry[i],
p = item.Parent,
target = !p ? roots : (children[p] || (children[p] = []));
target.push({ value: item });
}
// function to recursively build the tree
var findChildren = function(parent) {
if (children[parent.value.Id]) {
parent.children = children[parent.value.Id];
for (var i = 0, len = parent.children.length; i < len; ++i) {
findChildren(parent.children[i]);
}
}
};
// enumerate through to handle the case where there are multiple roots
for (var i = 0, len = roots.length; i < len; ++i) {
findChildren(roots[i]);
}
return roots;
}
console.log(buildHierarchy(items));
这是另一个。 这应该适用于多个根节点:
function convertToHierarchy() {
var arry = [{ "Id": "1", "Name": "abc", "Parent": "" },
{ "Id": "2", "Name": "abc", "Parent": "1" },
{ "Id": "3", "Name": "abc", "Parent": "2" },
{ "Id": "4", "Name": "abc", "Parent": "2"}];
var nodeObjects = createStructure(arry);
for (var i = nodeObjects.length - 1; i >= 0; i--) {
var currentNode = nodeObjects[i];
//Skip over root node.
if (currentNode.value.Parent == "") {
continue;
}
var parent = getParent(currentNode, nodeObjects);
if (parent == null) {
continue;
}
parent.children.push(currentNode);
nodeObjects.splice(i, 1);
}
//What remains in nodeObjects will be the root nodes.
return nodeObjects;
}
function createStructure(nodes) {
var objects = [];
for (var i = 0; i < nodes.length; i++) {
objects.push({ value: nodes[i], children: [] });
}
return objects;
}
function getParent(child, nodes) {
var parent = null;
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].value.Id == child.value.Parent) {
return nodes[i];
}
}
return parent;
}
虽然上述解决方案确实有效- 我认为它们非常慢并且没有经过太多循环和过时方法的优化(我们将使用ES6语法)。 我建议使用波纹管优化解决方案,这将为您带来性能提升。 阅读此博客文章以了解其工作原理。
javascript
const hierarchy = (data) => { const tree = []; const childOf = {}; data.forEach((item) => { const { Id, Parent } = item; childOf[Id] = childOf[Id] || []; item.children = childOf[Id]; Parent ? (childOf[Parent] = childOf[Parent] || []).push(item) : tree.push(item); }); return tree; }; // print console.log(hierarchy([{"Id":"1", "Name":"abc", "Parent":""}, {"Id":"2", "Name":"abc", "Parent":"1"}, {"Id":"3", "Name":"abc", "Parent":"2"},{"Id":"4", "Name":"abc", "Parent":"2"}], { idKey: 'Id', parentKey: 'Parent' }));
这里有一些结果和其他海报之间的比较
如果您正在寻找带有参数的版本以获得更动态但速度稍慢的版本,则如下所示:
const hierarchy = (data = [], { idKey = 'id', parentKey = 'parentId', childrenKey = 'children' } = {}) => {
const tree = [];
const childrenOf = {};
data.forEach((item) => {
const { [idKey]: id, [parentKey]: parentId = 0 } = item;
childrenOf[id] = childrenOf[id] || [];
item[childrenKey] = childrenOf[id];
parentId ? (childrenOf[parentId] = childrenOf[parentId] || []).push(item) : tree.push(item);
});
return tree;
}
快乐黑客
我会做这样的事情。 它处理多个根节点,并且在 IMO 中具有相当的可读性。
array = [{"Id":"1", "Name":"abc", "Parent":""},
{"Id":"2", "Name":"abc", "Parent":"1"},
{"Id":"3", "Name":"abc", "Parent":"2"},
{"Id":"4", "Name":"abc", "Parent":"2"},
{"Id":"5", "Name":"abc", "Parent":""},
{"Id":"6", "Name":"abc", "Parent":"5"}];
function buildHierarchy(source)
{
Array.prototype.insertChildAtId = function (strId, objChild)
{
// Beware, here there be recursion
found = false;
for (var i = 0; i < this.length ; i++)
{
if (this[i].value.Id == strId)
{
// Insert children
this[i].children.push(objChild);
return true;
}
else if (this[i].children)
{
// Has children, recurse!
found = this[i].children.insertChildAtId(strId, objChild);
if (found) return true;
}
}
return false;
};
// Build the array according to requirements (object in value key, always has children array)
var target = [];
for (var i = 0 ; i < array.length ; i++)
target.push ({ "value": source[i], "children": []});
i = 0;
while (target.length>i)
{
if (target[i].value.Parent)
{
// Call recursion to search for parent id
target.insertChildAtId(target[i].value.Parent, target[i]);
// Remove node from array (it's already been inserted at the proper place)
target.splice(i, 1);
}
else
{
// Just skip over root nodes, they're no fun
i++;
}
}
return target;
}
console.log(buildHierarchy(array));
在 ES6 中实现,带有一个简单的示例输入。 可以在浏览器控制台测试
let array = [{ id: 'a', children: ['b', 'c'] }, { id: 'b', children: [] }, { id: 'c', children: ['b', 'd'] }, { id: 'd', children: ['b'] }],
tree = (data) => {
let nodes = Object.create(null),
result = {};
data.forEach((item) => {
if (!nodes[item.id]) {
nodes[item.id] = {id: item.id, children: []}
result = nodes
}
item.children.forEach((child) => {
nodes[child] = {id: child, children: []}
nodes[item.id].children.push(nodes[child])
})
})
return result
}
console.log(tree(array))
伙计们,如果我在 nodejs 中使用它并且需要创建嵌套的 ul/li 而不是 json 呢? 可以请写代码吗
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.