将平面结构转换为分层结构

Converting flat structure to hierarchical

本文关键字:结构 分层 转换 平面      更新时间:2023-09-26

我需要创建能够将平面对象转换为递归对象的函数。下面是我的例子:我有一个平面数组:

var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
]

结果应该是:

recursiveArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
        Children: [
            {
                Description: "Z",
                guid: "b1113b35",
                parent: "c8e63b35",
                Children: [
                    {
                        Description: "F",
                        guid: "d2cc2233",
                        parent: "b1113b35",
                    }
                ]
            }, 
        ]
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
        Children: [
        {
            Description: "K",
            guid: "cd3b11caa",
            parent: "a24a3b1a",
        }
    }
]

请帮我找出做这件事的方法。一个工作算法将受到赞赏,因为我有问题,了解如何正确地做到这一点。在每种情况下,我都需要找到递归结构中检查元素的特定位置,并将其推入查找元素子数组。我认为这是愚蠢和低效的。有什么方法可以快速有效地做到这一点吗?

编辑:递归数组格式错误。现在应该没问题了。我的数组没有排序

这个效果很好,很容易阅读:

function flatToHierarchy (flat) {
    var roots = [] // things without parent
    // make them accessible by guid on this map
    var all = {}
    flat.forEach(function(item) {
      all[item.guid] = item
    })
    // connect childrens to its parent, and split roots apart
    Object.keys(all).forEach(function (guid) {
        var item = all[guid]
        if (item.parent === null) {
            roots.push(item)
        } else if (item.parent in all) {
            var p = all[item.parent]
            if (!('Children' in p)) {
                p.Children = []
            }
            p.Children.push(item)
        }
    })
    // done!
    return roots
}

我是这样做的:

var flatArray = [{
    Description: "G",
    guid: "c8e63b35",
    parent: null,
}, {
    Description: "Z",
    guid: "b1113b35",
    parent: "c8e63b35",
}, {
    Description: "F",
    guid: "d2cc2233",
    parent: "b1113b35",
}, {
    Description: "L",
    guid: "a24a3b1a",
    parent: null,
}, {
    Description: "K",
    guid: "cd3b11caa",
    parent: "a24a3b1a",
}];
var recursiveArray = unflatten(flatArray);
alert(JSON.stringify(recursiveArray, null, 4));
<script>
function unflatten(items) {
    return items.reduce(insert, {
        res: [],
        map: {}
    }).res;
}
function insert(obj, item) {
    var parent     = item.parent;
    var map        = obj.map;
    map[item.guid] = item;
    if (parent === null) obj.res.push(item);
    else {
        var parentItem = map[parent];
        if (parentItem.hasOwnProperty("Children"))
            parentItem.Children.push(item);
        else parentItem.Children = [item];
    }
    return obj;
}
</script>

当然,这只适用于你的flatArray具有每个父级出现在其子级之前的属性。

希望对你有帮助。

我尝试用伪代码编写算法,最终得到的JS代码几乎可以工作(可能需要一些额外的验证/检查),但显示了解决问题的一般方法。

//Lets separate children (nodes with a parent) from roots (nodes without a parent)
var children = flatArray.filter(function(object){
    return object.parent !== null;
});
var roots = flatArray.filter(function(object){
    return object.parent === null;
});
//And add each child to the nodes tree
children.foreach(function(child){
    recursiveAdd(roots, child);
});
//To add a children node, node tree is searched recursively for a parent
function recursiveAdd(nodes, child){
    nodes.foreach(function(parent){
        if(parent.guid === child.parent){
            parent.Children = parent.Children | [];
            parent.Children.add(child);
        } else if(parent.Children) {
            recursiveAdd(parent.Children, child);
        }
    });
}
//Temporary children array can be garbage collected
children = null;
//Resulting node tree
var recursiveArray = roots;

这个递归函数可能对你有好处:

var flatArray = [{
  Description: "G",
  guid: "c8e63b35",
  parent: null,
  Children: []
}, {
  Description: "Z",
  guid: "b1113b35",
  parent: "c8e63b35",
  Children: []
}, {
  Description: "F",
  guid: "d2cc2233",
  parent: "b1113b35",
  Children: []
}, {
  Description: "L",
  guid: "a24a3b1a",
  parent: null,
  Children: []
}, {
  Description: "K",
  guid: "cd3b11caa",
  parent: "a24a3b1a",
  Children: []
}, ];

for (var i = 0; i < flatArray.length; i++) {
  recursive(flatArray[i]);
}
function recursive(a) {
  for (var i = 0; i < flatArray.length; i++) {
    if (flatArray[i].parent == a.guid) {
      var b = flatArray[i];
      recursive(b);
      a.Children.push(b);
    }
  }
}
console.log(flatArray)

var flatArray = [
    {
        Description: "G",
        guid: "c8e63b35",
        parent: null,
    },
    {
        Description: "Z",
        guid: "b1113b35",
        parent: "c8e63b35",
    },
    {
        Description: "F",
        guid: "d2cc2233",
        parent: "b1113b35",
    },
    {
        Description: "L",
        guid: "a24a3b1a",
        parent: null,
    },
    {
        Description: "K",
        guid: "cd3b11caa",
        parent: "a24a3b1a",
    },      
];
//for printing
function htmlPrint(obj) {
  document.write('<pre>'+JSON.stringify(obj,null,2)+'</pre>');
};
var guids = {};
var roots = [];
flatArray.forEach(function(node){ 
  guids[node.guid] = node;       //save into a hash
  node.Children = [];            //make sure it has a children array
  //save it as root if it is a root
  if(node.parent === null){ roots.push(node);}
});
flatArray.forEach(function(node){ 
  //if it has a parent, add self to parent's children
  var parent = guids[node.parent]; 
  if(parent)  parent.Children.push(node); 
});
htmlPrint(roots);

你可以在Angular中使用下面的代码:

flatToHierarchy(flat: any[], parent: any = null, Key: string = 'id', parentKey: string = 'parentId') {
  var leafs: any = [];
  if (!parent) {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === null);
  } else {
    leafs = flat.filter((x: { [x: string]: any; }) => x[parentKey] === parent[Key]);
  }
  if (!leafs || leafs.length == 0) {
    return;
  } else {
      leafs.forEach((item: { children: any[]; }) => {
      item.children = [];
      item.children = this.flatToHierarchy(flat, item);
    });
  }
  return leafs;
}

像这样使用

 this.flatToHierarchy(flatItems);