提示:将扁平化数组转换成树形数组
1、使用递归的方法
function buildTree(arr, parentId = null) {
const result = [];
const temp = arr.filter(item => item.parent_id=== parentId);
temp.forEach(item => {
const children = buildTree(arr, item.id);
if (children.length) {
item.children = children;
}
result.push(item);
});
return result;
}
const arr = [
{id: 4, parent_id: 3},
{id: 'aa', parent_id: 'a'},
{id: 1, parent_id: null},
{id: 3, parent_id: 2},
{id: 'a', parent_id: 'a0'},
{id: 2, parent_id: 1},
{id: 'a0', parent_id: null}
];
const tree = buildTree(arr);
console.log(JSON.stringify(tree, null, 2));
2、使用reduce 方法
const tree = arr.reduce((o, i) => {
i = Object.assign(o[i.id] ??= {}, i);
((o[i.pid ?? ''] ??= {}).children ??= []).push(i);
return o;
}, {})['']?.children
console.log(tree);
注意事项:
?? 是一个逻辑运算符,用于判断左侧操作数是否为null或undefined。如果是,则返回右侧操作数,否则返回左侧操作数的值。
示例:
let x = null ?? 'default';
let y = 'value' ?? 'default';
let z = 0 ?? 42;
??= 是 ?? 运算符的赋值形式,用于在左侧操作数为 null 或 undefined 时,将右侧操作数赋值给左侧操作数。
示例:
let a = null;
a ??= 'default';
let b = 'value';
b ??= 'default';
3、使用forEach方法
要根据每个元素的父子关系重新构建数据结构
假设有一个扁平化数组,每个元素包含 id 和 parent_id 字段来表示父子关系,树形数组的每个节点包含 id 和 children 字段。
function buildTree(flatArray) {
let idMap = {};
flatArray.forEach(item => {
idMap[item.id] = { ...item, children: [] };
});
let tree = [];
flatArray.forEach(item => {
if (item.parent_id !== null && idMap[item.parent_id]) {
idMap[item.parent_id].children.push(idMap[item.id]);
} else {
tree.push(idMap[item.id]);
}
});
return tree;
}
let flatArray = [
{ id: 1, parent_id: null },
{ id: 2, parent_id: 1 },
{ id: 3, parent_id: 1 },
{ id: 4, parent_id: 2 },
{ id: 5, parent_id: 3 },
{ id: 6, parent_id: null },
{ id: 7, parent_id: 6 },
];
let treeArray = buildTree(flatArray);
console.log(treeArray);
4. 迭代方法
迭代方法通常使用循环和字典(或者 Map)来实现。它适用于处理层级不深、数据量较大的情况。
function buildTree(arr) {
const tree = {};
const map = {};
arr.forEach(node => {
map[node.id] = { ...node, children: [] };
});
Object.values(map).forEach(node => {
if (node.parent_id) {
map[node.parent_id].children.push(node);
} else {
tree[node.id] = node;
}
});
return Object.values(tree);
}
5.使用递归和映射(Map)
结合递归和 Map 对象,可以避免多次遍历和条件判断,提高效率。
function buildTree(arr) {
const map = new Map();
const tree = [];
arr.forEach(node => map.set(node.id, { ...node, children: [] }));
map.forEach(node => {
if (node.parent_id) {
const parent = map.get(node.parent_id);
if (parent) {
parent.children.push(node);
}
} else {
tree.push(node);
}
});
return tree;
}
6.使用 reduce 函数
利用数组的 reduce 方法,可以更紧凑地实现树的构建过程。
function buildTree(arr) {
const map = {};
const tree = [];
arr.reduce((acc, node) => {
map[node.id] = { ...node, children: [] };
if (node.parent_id === null) {
tree.push(map[node.id]);
} else {
const parent = map[node.parent_id];
if (parent) {
parent.children.push(map[node.id]);
}
}
return acc;
}, []);
return tree;
}
7.使用深度优先搜索(DFS)
深度优先搜索是一种递归的算法,通过遍历数组并递归构建树,可以有效地将扁平化数组转换为树形结构。
function buildTree(arr) {
const tree = {};
const dfs = (parentId) => {
return arr
.filter(node => node.parent_id === parentId)
.map(node => ({ ...node, children: dfs(node.id) }));
};
tree.children = dfs(null);
return tree.children;
}
8.使用广度优先搜索(BFS)
广度优先搜索可以通过迭代实现,使用队列来逐层构建树形结构,适合于需要按层级构建树的情况。
function buildTree(arr) {
const tree = [];
const queue = [{ id: null, children: tree }];
while (queue.length > 0) {
const { id, children } = queue.shift();
arr.forEach((node) => {
if (node.parent_id === id) {
const newNode = { ...node, children: [] };
children.push(newNode);
queue.push(newNode);
}
});
}
return tree;
}
总结
提示:这里对文章进行总结:
递归 vs 迭代:递归方法通常直观且易于理解,但在处理大量数据时可能会有性能上的挑战,可能面临堆栈溢出或性能问题。迭代方法和使用 Map 的方法通常可以提供更好的性能,特别是在数据量较大或层级较深的情况下,通常更适合处理大数据量和层级深的情况。选择合适的方法可以根据实际需求进行权衡和调整。
DFS vs BFS:DFS适合递归实现,BFS适合迭代实现。选择取决于数据结构和需求,例如是否需要按层级构建树。