示例數據:
const arr = [{ id: 1, parentId: null, name: 'Root' },{ id: 2, parentId: 1, name: 'Child 1' },{ id: 3, parentId: 1, name: 'Child 2' },{ id: 4, parentId: 2, name: 'Grandchild 1' },
]
目標生成:
const tree = [{id: 1,name: 'Root',children: [{id: 2,name: 'Child 1',children: [{ id: 4, name: 'Grandchild 1', children: [] }],},{id: 3,name: 'Child 2',children: [],},],},
]
遞歸
function arrayToTree(arr, pid) {let res = [];arr.forEach((item) => {// parentId 等于null即為根節點if (item.parentId === pid) {// 遞歸查找當前節點的所有子節點const children = arrayToTree(arr, item.id);item.children = children;res.push(item);}});return res;
}let result = arrayToTree(arr, null)
console.log(result);
時間復雜度較高O(n^2)
Map
時間復雜度O(n)
function arrayToTree(arr) {const map = new Map();// 使用Map,查找父節點時間復雜度為O(1)const res = []; // 返回根節點數組// 第一次遍歷:將所有節點存入Map(id作為鍵,節點對象作為值)for (const item of arr) {map.set(item.id, item);// 注意:這里存儲的是原始對象的引用}// 第二次遍歷:構建樹結構for (const item of arr) {if (item.parentId=== null) { // 根節點res.push(item);} else { // 有父節點,// 查找當前節點的父節點const parent = map.get(item.parentId);if (!parent.children) {parent.children = [];}// 將當前節點添加到父節點的children數組parent.children.push(item);}}return res;
}
避免修改原始數據的一版本
// 此版本通過創建節點副本避免修改原始數據,
function arrayToTree(arr) {const map = new Map();const res = [];// 第一步:創建所有節點的副本(含children屬性)for (const item of arr) {// 使用展開運算符{...item}創建淺拷貝,避免直接修改原始數據map.set(item.id, { ...item, children: [] });}// 第二步:構建樹結構for (const item of arr) {// 從Map獲取當前節點對應的副本,注意:這里使用副本節點而非原始數據const node = map.get(item.id); if (item.parentId === null) { // 根節點res.push(node);} else {// 獲取父節點副本const parent = map.get(item.parentId);// 將當前節點添加到父節點的children數組parent.children.push(node); }} return res;
}
參考:
大廠JS筆試__數組與樹互轉
高頻面試題:樹結果轉為數組,數組轉為樹(詳細解答,性能提升之王)
大廠JS筆試__數組與樹互轉