方法一:
//函数定义
//参数说明:treeData为一维数组,id为节点id,pId为节点的父id,rootPId为根节点id(一般为0)
transTree = (treeData, { id, pId, rootPId }) => {
const keyNodes = {};
const rootNodeList = [];
// Fill in the map
const nodeList = treeData.map(node => {
const clone = { ...node };
const key = clone[id];
keyNodes[key] = clone;
clone.key = clone.key || key;
return clone;
});
// Connect tree
nodeList.forEach(node => {
const parentKey = node[pId];
const parent = keyNodes[parentKey];
// Fill parent
if (parent) {
parent.children = parent.children || [];
parent.children.push(node);
}
// Fill root tree node
if (parentKey === rootPId || (!parent && rootPId === null)) {
rootNodeList.push(node);
}
});
return rootNodeList;
}
//函数调用
transTree(data, { id: 'id', pId: 'parentId', rootPId: 0 })
转换算法实现思路:
- 将一维数组转换为key => value的对象格式
- 遍历deepClone的treeData
- 将每个节点进行向上查找父节点
- 如果父节点的pId === rootValue(根节点id),那么该父节点为一级节点,将该节点push到父节点的children中
- 最后通过单次forEach实现了一维数组对树形结构数据的转换
参考链接:一维数组转换为树形结构数据(效率算法)_Destiny_Chan的博客-CSDN博客_一维数组转树
方法二:map提供映射,便于查找
遍历数组,生成tree node 并加入map
找到parent node 并加入他的children
function convert(arr) {
// 用于 id 和 treeNode 的映射
const idToTreeNode = new Map();
let root = null;
arr.forEach(item => {
const { id, name, parentId } = item;
// 定义 tree node 并加入 map
const treeNode = { id, name };
idToTreeNode.set(id, treeNode);
// 找到 parentNode 并加入到它的 children
const parentNode = idToTreeNode.get(parentId);
if (parentNode) {
if (parentNode.children == null)
parentNode.children = [];
parentNode.children.push(treeNode);
}
// 找到根节点
if (parentId === 0)
root = treeNode;
});
return root;
}
const arr = [
{ id: 1, name: '部门A', parentId: 0 },
{ id: 2, name: '部门B', parentId: 1 },
{ id: 3, name: '部门C', parentId: 1 },
{ id: 4, name: '部门D', parentId: 2 },
{ id: 5, name: '部门E', parentId: 2 },
{ id: 6, name: '部门F', parentId: 3 },
];
const tree = convert(arr);
console.info(tree);
广度优先遍历:树转平级
function convert1(root) {
// Map
const nodeToParent = new Map();
const arr = [];
// 广度优先遍历,queue
const queue = [];
queue.unshift(root); // 根节点 入队
while (queue.length > 0) {
const curNode = queue.pop(); // 出队
if (curNode == null)
break;
const { id, name, children = [] } = curNode;
// 创建数组 item 并 push
const parentNode = nodeToParent.get(curNode);
const parentId = (parentNode === null || parentNode === void 0 ? void 0 : parentNode.id) || 0;
const item = { id, name, parentId };
arr.push(item);
// 子节点入队
children.forEach(child => {
// 映射 parent
nodeToParent.set(child, curNode);
// 入队
queue.unshift(child);
});
}
return arr;
}
const obj = {
id: 1,
name: '部门A',
children: [
{
id: 2,
name: '部门B',
children: [
{ id: 4, name: '部门D' },
{ id: 5, name: '部门E' }
]
},
{
id: 3,
name: '部门C',
children: [
{ id: 6, name: '部门F' }
]
}
]
};
const arr1 = convert1(obj);
console.info(arr1);