在平时的业务中,相信咱们必定都遇到过须要将后台传过来的一维数组转为树结构。javascript
var arr = [
{ id: 1, pid: -1, },
{ id: 2, pid: -1, },
{ id: 3, pid: -1, },
{ id: 4, pid: 1, },
{ id: 5, pid: 2, },
{ id: 6, pid: 3, },
{ id: 7, pid: 5, },
];
// 转换以后的树结构
var tree = [
{
"id": 1,
"pid": -1,
"children": [
{
"id": 4,
"pid": 1
}
]
},
{
"id": 2,
"pid": -1,
"children": [
{
"id": 5,
"pid": 2,
"children": [
{
"id": 7,
"pid": 5
}
]
}
]
},
{
"id": 3,
"pid": -1,
"children": [
{
"id": 6,
"pid": 3
}
]
}
]
复制代码
前提是层级固定,好比最多只有3层,那么咱们可能会写出以下的代码:java
var arr = [
{ id: 1, pid: -1, },
{ id: 2, pid: -1, },
{ id: 3, pid: -1, },
{ id: 4, pid: 1, },
{ id: 5, pid: 2, },
{ id: 6, pid: 3, },
{ id: 7, pid: 5, },
];
function toTree(data) {
var arr = [...data];
// 查找一级
var tree = arr.filter(v => v.pid == -1);
tree.map(v => {
// 查找二级
v.children = arr.filter(({ pid }) => pid == v.id);
if (v.children.length == 0) delete v.children;
else {
v.children.map(item => {
// 查找三级
item.children = arr.filter(({ pid }) => pid == item.id);
if (item.children.length == 0) delete item.children;
return item;
});
}
return v;
});
return tree;
}
JSON.stringify(toTree(arr), null, ' ')
复制代码
若是树结构的层级固定,咱们能够直接使用filter和map经过简单的循环来生成树结构。如此一来,代码是简单了,效率却很是的低,由于肯定每一个元素的父子关系时都得遍历一次原数组,当原数组很长的时候,执行时间就会变得很长。数组
另一个问题就是,树结构的层级被写死了,当树结构的深度大于咱们的层级时,没法递归生成树结构。函数
那么,怎么解决这个问题呢?ui
后来我想到了一个办法,就是spa
这里主要使用的就是数组的splice方法,让一维数组逐渐减小,直至数组为空结束递归。code
还有一个知识点就是**使用了对象的引用,在没有拷贝对象的状况,修改函数传过去的对象就能够改变原对象。**平时讲究的都是纯函数,这里则刚好借助了非纯函数。对象
代码以下:递归
var arr = [
{ id: 1, pid: -1, },
{ id: 2, pid: -1, },
{ id: 3, pid: -1, },
{ id: 4, pid: 1, },
{ id: 5, pid: 2, },
{ id: 6, pid: 3, },
{ id: 7, pid: 5, },
];
// @params data 一维数组
// @params pid 父id字段名
// @params id id字段名
// @return Array
function toTree(data, pid = 'pid', id = 'id') {
var arr = [...data];
var tree = [];
for(let i = 0, len = arr.length; i < len; i++) {
if (arr[i][pid] == -1) {
tree.push(arr[i]);
arr.splice(i, 1);
// 这里注意,删除数组的其中一个元素后,下次索引不要 +1 便可访问下一个元素,因此这里 -1 以抵消 +1
i = i - 1;
}
}
getChild(arr, tree, pid, id);
return tree;
}
function getChild(originArr, mapArr, pid, id) {
// 若是一维数组为空,则结束递归
if (originArr.length == 0 || !mapArr) return ;
for(let j = 0, len = mapArr.length; j < len; j++) {
let item = mapArr[j];
item.children = [];
for(let i = 0; i < originArr.length; i++) {
let obj = originArr[i];
if (item[id] == obj[pid]) {
item.children.push(obj);
originArr.splice(i, 1);
// 这里注意,删除数组的其中一个元素后,下次索引不要 +1 便可访问下一个元素,因此这里 -1 以抵消 +1
i = i - 1;
}
}
// 若是没有子节点,就删除children属性
if (item.children.length == 0) {
delete item.children;
} else {
// 若是有子节点,就递归获取下一级子节点
getChild(originArr, item.children, pid, id);
}
}
}
toTree(arr)
JSON.stringify(toTree(arr), null, ' ')
复制代码