引言
扁平数组与树形结构两种形式的数据相互转换在项目中经常使用,包括菜单栏的数据组装等,这里记录下转换的方法。
扁平数组转换为树形结构
这个是最常用的,当我们从后台获取一个扁平数组的时候,通常比如用id、pid来标识父子关系,如:
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"}]
需要注意的
我们这里面都是直接对原数组进行转换,但同时也修改了原数据,因此最好对传入数据进行一个深拷贝,扁平数组转树形数据的时候可以简单点一般浅拷贝就行。