1. 数组转换树形结构(树形结构还原)
源数据:默认数据根据pid排列好
const list = [
{ id: 1, name: '部门1', pid: 0 },
{ id: 2, name: '部门2', pid: 1 },
{ id: 3, name: '部门3', pid: 1 },
{ id: 4, name: '部门4', pid: 3 },
{ id: 5, name: '部门5', pid: 4 },
];
转换后数据
const tree = [
{
"id": 1,
"name": "部门1",
// "pid": 0,
"children": [
{
"id": 2,
"name": "部门2",
// "pid": 1
},
{
"id": 3,
"name": "部门3",
// "pid": 1,
"children": [
{
"id": 4,
"name": "部门4",
// "pid": 3,
"children": [
{
"id": 5,
"name": "部门5",
// "pid": 4
}
]
}
]
}
]
}
]
1.1 递归遍历
const listToTree = (data, parentId = 0) => {
// 收集结果
const result = []
// 递归遍历
data.forEach(item => {
// 找到相同的pid进行下一层遍历
if (item.pid === parentId) {
const children = listToTree(data, item.id)
if (children.length) {
item.children = children
}
result.push(item)
}
});
return result
}
1.2 通过Map对象实现
循环遍历原始数组,使用对象存储每个节点,并将子节点添加到父节点的children数组中。
function listToTree(data) {
const map = new Map(); // 用于存储每个节点的引用
const result = [];
// 将每个节点存入Map对象
data.forEach(item => {
map.set(item.id, Object.assign(item, { children: [] }));
});
// 遍历每个节点,将其挂载到其父节点的children属性下
map.forEach(item => {
if (item.pid === 0) {
result.push(item);
} else {
const parent = map.get(item.pid);
parent.children.push(item);
}
});
return result;
}
初始化存储的节点
遍历后的数据
1.3 对象一次遍历
直接在2的基础上,不需要做两次遍历,
function listToTree2(data){
// 创建对象 map 和数组 result,分别用来存储所有节点和根节点
const map = {}
const result = []
data.forEach(item=>{
//遍历数据,并将每个节点保存到 map 对象中
map[item.id] = item
if(item.pid===0){
result.push(item)
}else{
const parent = map[item.pid]
if(!parent.children){
parent.children = []
}
parent.children.push(item)
}
})
return result
}
2. 树形结构扁平化
深度优先和广度优先是两种常见的遍历算法。
- 深度优先遍历是优先探索深度,直到无法再深入为止
- 广度优先遍历是优先探索广度,即从离起始节点最近的节点开始扩展。
2.1 深度优先遍历(Depth First Search, DFS)
基本思路:
- 创建一个空数组result用来存储扁平化后的结果。
- 编写一个dfs函数,函数接收两个参数node和parentId,分别表示当前遍历到的节点和其父节点的id。
- 在dfs函数内,首先将当前节点的信息{id, name, pid} push到result数组中。
- 然后判断当前节点是否存在子节点,若存在,则递归调用dfs方法,将子节点和当前节点的id作为参数传入。在递归调用前需判断当前节点是否有pid,若无则默认为0。
- 最后返回result数组即可。
/** 深度优先 */
function flattenTree(tree) {
const result = []
function dfs(node, parentId) {
result.push({
id: node.id,
name: node.name,
pid: parentId || 0
})
if (node.children && node.children.length) {
node.children.forEach(child => dfs(child, node.id))
}
}
tree.forEach(node => dfs(node))
return result
}
2.2 广度优先遍历(Breadth First Search, BFS)
基本思路:
- 创建一个空数组result用于存储扁平化后的结果。
- 将树的根节点放入一个队列中
- 遍历队列中的节点,分别将节点信息{id, name, pid} push到结果数组中。同时,若当前节点存在子节点,则将子节点依次放入队列末尾。
- 当队列为空时,即可结束循环遍历,返回结果数组。
/** 广度优先 */
function flattenTreeBFS(tree) {
const result = []
const queue = []
tree.forEach(node => queue.push(node))
while (queue.length) {
const node = queue.shift()
result.push({
id: node.id,
name: node.name,
pid: node.parentId || 0
})
if (node.children && node.children.length) {
node.children.forEach(child => queue.push({ ...child, parentId: node.id }))
}
}
return result
}