题目:
const flatArr = [
{ id: 1, name: '爷爷1', pid: 0 },
{ id: 2, name: '爷爷2', pid: 0 },
{ id: 123, name: '儿子1', pid: 12 },
{ id: 211, name: '儿子2', pid: 21 },
{ id: 11, name: '爸爸1', pid: 1 },
{ id: 12, name: '爸爸2', pid: 1 },
{ id: 21, name: '爸爸3', pid: 2 },
]
function transform (arr) {
// ...
}
const resultArr = transform (flatArr)
console.log(resultArr)
[
{
id: 1,
name: '爷爷1',
pid: 0,
children: [
{
id: 11,
name: '爸爸1',
pid: 1
},
{
id: 12,
name: '爸爸2',
pid: 1,
chilren: [
{ id: 123, name: '儿子1', pid: 12 },
]
},
]
},
{
id: 2,
name: '爷爷2',
pid: 0,
children: [
{
id: 21,
name: '爸爸3',
pid: 2,
chilren: [
{ id: 211, name: '儿子2', pid: 21 }
]
},
]
},
]
思路:涉及树状结构先想递归,拆解步骤,按题目给出的数组来看,循环先将pid为0的元素放入结果数组中,放入的这一刻查询该元素的id是否为数组其他元素的pid,如果是,循环找出这些元素将其放入该元素的children中,以此做深度遍历。
递归解答:
function transform (arr) {
function func (arr, pid) {
const res = []
for (const elem of arr) {
if (elem.pid === pid) {
res.push(elem)
if (arr.find(item => item.pid === elem.id)) {
res[res.length - 1].children = func(arr, elem.id)
}
}
}
return res
}
return func(arr, 0)
}
结果打印出来确实是能实现题目要求的结果,但是在实际的开发中,我们的树形结构往往属性众多且复杂,递归会占用很大的内存,也可能会造成栈溢出,所以我又想了其他的一些解法
利用“引用类型浅拷贝”解答:
function transform (arr) {
arr.forEach(elem => {
if (elem.pid) {
const pIndex = arr.findIndex(item => item.id === elem.pid)
if (pIndex > -1 && !arr[pIndex].children) {
arr[pIndex].children = []
}
arr[pIndex].children.push(elem)
}
})
return arr.filter(item => !item.pid)
}
根据上面的解答,我们再进行进一步的优化,通过提前声明一个结果数组来代替最后的filter循环,通过提前将id与索引值建立map联系来减少原本forEach里面还要每次再通过findIndex来找父元素的循环,优化如下:
function transform (arr) {
const results = []
const map = new Map(arr.map((item, index) => [item.id, index]))
for (let i = 0; i < arr.length; i++) {
if (arr[i].pid) {
const index = map.get(arr[i].pid)
arr[index].children = Array.isArray(arr[index].children)
? [...arr[index].children, arr[i]]
: [arr[i]]
continue
}
results.push(arr[i])
}
return results
}