树形结构数据的处理笔试时经常会出现,本文将以文件路径转化为树形结构为例:
一、需求与目标
1.1 待处理数据
const paths = [
'/Users/77952/Desktop/grow/工作/钧仔简历/面试项目',
'/Users/77952/Pictures/吞噬星空.jpg',
'/Users/77952/Desktop/grow/医学文件'
];
1.2 目标
{
"id": 1,
"name": "Users",
"parent_id": null,
"children": [{
"id": 2,
"name": "77952",
"parent_id": 1,
"children": [{
"id": 3,
"name": "Desktop",
"parent_id": 2,
"children": [{
"id": 4,
"name": "grow",
"parent_id": 3,
"children": [{
"id": 5,
"name": "工作",
"parent_id": 4,
"children": [{
"id": 6,
"name": "钧仔简历",
"parent_id": 5,
"children": [{
"id": 7,
"name": "面试项目",
"parent_id": 6,
"children": []
}]
}]
},
{
"id": 10,
"name": "医学文件",
"parent_id": 4,
"children": []
}
]
}]
},
{
"id": 8,
"name": "Pictures",
"parent_id": 2,
"children": [{
"id": 9,
"name": "吞噬星空.jpg",
"parent_id": 8,
"children": []
}]
}
]
}]
}
二、实现过程
2.1 第一步:初始化Map数据结构 + 唯一节点id
//1.初始化数据结构 + 唯一节点id
const nodesMap = new Map()
let idCounter = 1 //唯一节点id
2.2 第二步:遍历 => 分割路径 + 过滤空串 + 创建节点 + 更新父节点id
paths.forEach(path => {
const parts = path.split('/').filter(Boolean)
let parent_id = null //最外层父节点
parts.forEach(part => {
const node = createNode(part, parent_id)
parent_id = node.id // 更新父节点id
})
})
console.log("%c nodesMap", "color:red;", nodesMap);
// 创建节点
function createNode(name, parent_id) {
//文件夹名称可能会相同:检查节点是否已存在,如果存在,则返回该节点
for (let [_, node] of nodesMap) {
if (node.name === name && node.parent_id === parent_id) {
return node
}
}
//如果节点不存在,则创建新节点,并返回
const newNode = {
id: idCounter++,
name,
parent_id,
children: []
}
nodesMap.set(newNode.id, newNode)
return newNode
}
2.3 第三步:将节点转化为树形结构
// 树形结构辅助根数据
const result = {
name: 'root',
children: []
}
// 3.遍历 => 将节点转化为树形结构
for (let [_, node] of nodesMap) {
if (node.parent_id === null) {
result.children.push(node)
} else {
const parent = nodesMap.get(node.parent_id)
if (parent) {
parent.children.push(node)
}
}
}
第四步:返回树形结构数据
// 4.返回树形结构
return result.children[0]