前言
假设现在前端有个多级菜单需要渲染,但是菜单目录需要从服务器取得,但是服务器在数据库中存储的菜单数据是一张一维表格,表格中的每条数据通过pid来进行父级菜单的关联。
那么问题来了,前端渲染需要的是一个Tree的数据结构,但是我们从接口拿到的数据是一维数组的数据结构,要实现渲染,就需要转数据结构。
条件说明
- 每一条数据都有id和pid,id表示自身唯一的标记,pid则是指向父级id的地址;
- 最顶级的数据可能没有pid,但也有可能有pid,但是在数组中是唯一的,所以也应该作为最顶层的父级元素;
// 假设这是我们拿到的数据
const menuList = [
{
id: 11,
pid: 2,
name: 'list',
},
{
id: 2,
pid: 1,
name: 'ListBase',
},
{
id: 3,
pid: 11,
name: 'ListCard',
},
{
id: 4,
pid: 1,
name: 'form',
},
{
id: 5,
pid: 2,
name: 'FormBase',
},
{
id: 6,
pid: 11,
name: 'FormStep',
},
]
// 这是我们希望转换后的数据
[{
"id": 2,
"pid": 1,
"name": "ListBase",
"children": [{
"id": 11,
"pid": 2,
"name": "list",
"children": [{
"id": 3,
"pid": 11,
"name": "ListCard"
}, {
"id": 6,
"pid": 11,
"name": "FormStep"
}]
}, {
"id": 5,
"pid": 2,
"name": "FormBase"
}]
}, {
"id": 4,
"pid": 1,
"name": "form"
}]
方法一:递归
思路
- 原数组保持不变,生成一个新的tree数组,用来存储树形结构;
- 需要先在原数组的基础上,进行遍历,遍历的目标是,要找到列表中符合条件:pid在列表中唯一的全部数据;
- 将第二步的唯一值,放到我们的tree中,作为最顶层的数据;
- 然后对原列表进行遍历,在tree中找到满足条件的值(node.pid === treeNode.id),并对treeNode的children 进行插入操作;
- 对于找不到的项,在其children项中递归查询;
代码
function findNodeInTree(tree, node) {
tree.forEach(treeNode => {
if (treeNode.id === node.pid) {
const children = treeNode?.children || []
treeNode.children = [...children, node];
} else {
if (treeNode.children) {
findNodeInTree(treeNode.children, node)
}
}
})
}
function transform(list = []) {
let tree = []
const root = []
list.forEach((sNode) => {
const index = list.findIndex(node => sNode.pid === node.id)
if (index === -1) {
root.push(sNode)
}
})
tree = [...root]
list.forEach((node) => {
findNodeInTree(tree, node)
})
return tree
}
const res = transform(menuList)
const fs = require('fs')
fs.writeFile('./tree.json', JSON.stringify(res), (error) => {
error ? console.log(error) : console.log('success')
})
总结 核心就两个:找顶层数据;递归操作;
方法二:哈希表(散列表)
思路
- 新建一个map对象,用于存储对应的值;
- 遍历对象,判断条件,push内容,利用对象中的地址引用,快速实现树形结构的转换;
代码
function transform(list = []) {
const map = {};
list.forEach((node) => {
if (!map[node.id]) {
map[node.id] = { ...node, children: [] };
debugger;
} else {
map[node.id] = { ...node, children: map[node.id].children };
debugger;
}
if (node.pid && !map[node.pid]) {
map[node.pid] = { id: node.pid, children: [] };
debugger;
}
});
const tree = [];
Object.values(map).forEach((node) => {
if (node.pid) {
map[node.pid].children.push(node);
} else {
tree.push(node);
}
});
return tree;
}
const res = transform(menuList)
const fs = require('fs')
fs.writeFile('./tree.json', JSON.stringify(res), (error) => {
error ? console.log(error) : console.log('success')
})