Javascript:构建层次结构树

Javascript:构建层次结构树,javascript,Javascript,我的数据具有以下属性: 每个条目都有一个唯一的id(id) 每个都有一个父字段,该字段指向父字段的Id 一个节点可以有多个子节点,但只能有一个父节点 下面是我第一次尝试建一棵树。这是错误的,因为递归会导致无限循环。即使我解决了这个问题,我也不确定是否有更好的方法来解决这个问题。目前,我做了两次 我希望它是尽可能有效的,因为我有一个体面的数据量。它还需要动态重建树(根可以是任何节点) 以下程序中有示例数据: arry = [{"Id":"1", "Name":"abc", "Parent":""

我的数据具有以下属性:

  • 每个条目都有一个唯一的id(id)
  • 每个都有一个父字段,该字段指向父字段的Id
  • 一个节点可以有多个子节点,但只能有一个父节点
  • 下面是我第一次尝试建一棵树。这是错误的,因为递归会导致无限循环。即使我解决了这个问题,我也不确定是否有更好的方法来解决这个问题。目前,我做了两次

    我希望它是尽可能有效的,因为我有一个体面的数据量。它还需要动态重建树(根可以是任何节点)

    以下程序中有示例数据:

     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结构,节点作为字段“值”,子节点作为数组。)

    示例程序:

    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;
    }
    
    这里有一个解决方案:

    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));​
    
    var项目=[
    {“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”}
    ];
    函数构建层次结构(arry){
    变量根=[],子类={};
    //查找顶级节点并基于父节点散列子节点
    对于(变量i=0,len=arry.length;i
    这是另一个。这适用于多个根节点:

    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;
    }
    
    函数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);
    对于(var i=nodeObjects.length-1;i>=0;i--){
    var currentNode=节点对象[i];
    //跳过根节点。
    如果(currentNode.value.Parent==“”){
    继续;
    }
    var parent=getParent(当前节点、节点对象);
    如果(父项==null){
    继续;
    }
    parent.children.push(当前节点);
    节点对象拼接(i,1);
    }
    //节点对象中剩下的将是根节点。
    返回节点对象;
    }
    函数createStructure(节点){
    var对象=[];
    对于(var i=0;i
    我会这样做的。它处理多个根节点,在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));
    
    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”}];
    函数构建层次结构(源)
    {
    Array.prototype.insertchildataid=函数(strId,objChild)
    {
    //注意,这里有递归
    发现=错误;
    for(var i=0;ii)
    {
    if(目标[i].value.Parent)
    {
    //调用递归来搜索父id
    target.insertchildataid(target[i].value.Parent,target[i]);
    //从阵列中删除节点(已将其插入到正确的位置)
    靶剪接(i,1);
    }
    其他的
    {
    //只需跳过根节点,它们就没意思了
    i++;
    }
    }
    回报目标;
    }
    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))
    

    虽然上述解决方案确实有效-但我认为它们非常缓慢,没有太多的循环和过时的方法(我们将使用ES6语法)。我建议使用下面的优化解决方案,这将提高您的性能

    javascript

    const层次结构=(数据)=>{
    常量树=[];
    常量childOf={};
    data.forEach((项目)=>{
    const{Id,Parent}=项;
    childOf[Id]=childOf[Id]| |[];
    item.children=childOf[Id];
    帕
    
    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));
    
    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))