扁平数组与树形结构的转换——listToTree、treeToList

2,154 阅读2分钟

引言

扁平数组与树形结构两种形式的数据相互转换在项目中经常使用,包括菜单栏的数据组装等,这里记录下转换的方法。

扁平数组转换为树形结构

这个是最常用的,当我们从后台获取一个扁平数组的时候,通常比如用idpid来标识父子关系,如:

 var arr = [{id: 1, pid: '-1'},{id: 11, pid: '1'},{id: 12, pid: '1'},{id: 121, pid: '12'}]

一般这种涉及树结构的都可以考虑递归,一层一层计算:

 function listToTree(list, parent) {
     const out = []
     for (let node of list) {  
             if (node.pid == parent) {
                 // 递归
                 var children = listToTree(list, node.id)
                 if (children.length) {
                     node.children = children
                 }
                 out.push(node)
             }
     }
     return out
 }
 ​
 listToTree(arr, '-1') // [{"id":1,"pid":"-1","children":[{"id":11,"pid":"1"},{"id":12,"pid":"1","children":[{"id":121,"pid":"12"}]}]}]

但是项目中有个需求,在后台没有返回给带层级信息level的时候,需要用到层级信息,这样转换没法计算出层级,因此就需要传入一个level参数,默认根节点层级为0,依次递增:

 function listToTreeWithLevel(list, parent, level) {
     var out = []
     for (var node of list) {  
             if (node.pid == parent) {
                 node.level = level;
                 var children = listToTreeWithLevel(list, node.id, level + 1)
                 if (children.length) {
                     node.children = children
                 }
                 out.push(node)
             }ssss
     }
     return out
 }
 ​
 listToTreeWithLevel(arr, '-1', 0) //[{"id":1,"pid":"-1","children":[{"id":11,"pid":"1","level":1},{"id":12,"pid":"1","children":[{"id":121,"pid":"12","level":2}],"level":1}],"level":0}]

但是递归的方法需要后台返回的数据一定是规整的,假如有的不靠谱的后台给你返回一些根节点的pid很不规整,有的是-1,有的是0,还有的是null,这时候用递归就又问题了,不过通常我们还有另一种复杂度更低的处理方法,用Map记录的方法:

 function listToTree(arr) {
   const result = []
   const map = new Map()
   arr.forEach(item => {
     map.set(item.id, item)
   })
   for (let item of arr) {
     if (map.has(item.pid)) {
       if (!map.get(item.pid).children) {
         map.get(item.pid).children = []
       }
       map.get(item.pid).children.push(item)
     } else {
       result.push(item)
     }
   }
   return result
 }
 const arr = [{id: 1, pid: null, name: '1'},{id: 2, pid: '-1', name: '1'},{id: 11, pid: 1, name: '11'},{id: 12, pid: 1, name: '12'}]
 listToTree(arr); // [{"id":1,"pid":null,"name":"1","children":[{"id":11,"pid":1,"name":"11"},{"id":12,"pid":1,"name":"12"}]},{"id":2,"pid":"-1","name":"1"}]

树形结构转换为扁平数组

这个其实就是数据结构中的广度优先遍历:

 function treeToList(tree) {
   var queen = [];
   var out = [];
   queen = queen.concat(tree);
   while(queen.length) {
     var first = queen.shift();
     if (first.children) {
         // 下一层节点推入
         queen = queen.concat(first.children)
         delete first['children'];
     }
     out.push(first);
   }
   return out;
 }
 ​
 var tree = [{"id":1,"pid":"-1","children":[{"id":11,"pid":"1","children":[]},{"id":12,"pid":"1","children":[]}]}];
 treeToList(tree) //[{"id":1,"pid":"-1"},{"id":11,"pid":"1"},{"id":12,"pid":"1"}]

需要注意的

我们这里面都是直接对原数组进行转换,但同时也修改了原数据,因此最好对传入数据进行一个深拷贝,扁平数组转树形数据的时候可以简单点一般浅拷贝就行。