树形结构转列表
/* 题目描述: [ { id: 1, text: '节点1', parentId: 0, children: [ { id:2, text: '节点1_1', parentId:1 } ] } ] 转成 [ { id: 1, text: '节点1', parentId: 0 //这里用0表示为顶级节点 }, { id: 2, text: '节点1_1', parentId: 1 //通过这个字段来确定子父级 } ... ]*/function tree2List(treeData) { let result = [] const dfs = data => { data.forEach(item => { result.push(item) if(item.children) { dfs(item.children) delete item.children } }) } dfs(treeData) return result}var treeData = [ { id: 1, text: '节点1', parentId: 0, children: [ { id:2, text: '节点1_1', parentId:1 } ] }, { id: 2, text: '节点2', parentId: 0, children: [] }]tree2List(treeData)
export function flattenDeep<T extends {children: T[];}>(list: T[], newList?: T[]): T[] { if (!list.length) return list; if (!newList) newList = []; for (let index = 0; index < list.length; index++) { const item = list[index]; // 拷贝 item ;目的:删除 copyItem 的 children 推送到 newList 中,最终返回的 newList 中就没有了 children 字段 const itemDeleteChild = JSON.parse(JSON.stringify(item)); if (Reflect.deleteProperty(itemDeleteChild, "children")) { newList.push(itemDeleteChild); } if (item.children && !!item.children.length) { flattenDeep(item.children, newList); } } return newList;}
type TMap = Map< number, { parentId: number; label: string; }>;export function flattenDeepToMap< T extends { children: T[]; }>(list: T[], newList?: T[], tempMap?: TMap): TMap { if (!list.length) return new Map(); if (!newList) newList = []; if (!tempMap || !tempMap.size) tempMap = new Map(); for (let index = 0; index < list.length; index++) { const item = list[index]; // 拷贝 item ;目的:删除 copyItem 的 children 推送到 newList 中,最终返回的 newList 中就没有了 children 字段 const itemDeleteChild = JSON.parse(JSON.stringify(item)); if (Reflect.deleteProperty(itemDeleteChild, "children")) { tempMap.set(itemDeleteChild.id, { parentId: itemDeleteChild.parentId, label: itemDeleteChild.name, }); newList.push(itemDeleteChild); } if (item.children && !!item.children.length) { flattenDeepToMap(item.children, newList); } } console.log(`tempMap---->`, tempMap); return tempMap;}
列表转树形结构
let arr = [ { id: 1, name: "部门1", parentId: 0 }, { id: 2, name: "部门2", parentId: 1 }, { id: 3, name: "部门3", parentId: 1 }, { id: 4, name: "部门4", parentId: 3 }, { id: 5, name: "部门5", parentId: 1 },];/* 方法一:递归遍历查找(不推荐)时间复杂度为O(2^n) */function buildTree(list: any[], parentId: number, result: any[]) { for (let item of list) { if (item.parentId === parentId) { const newItem = { ...item, children: [] }; result.push(newItem); buildTree(list, item.id, newItem.children); // 重要,注意入参是:需要遍历的list , 需要挂载的当前ID , 需要挂载的当前 children } }}function FlattenToTree({ list, rootId }: { list: any[]; rootId: number }) { const result: any[] = []; buildTree(list, rootId, result); return result;}// console.log(JSON.stringify(FlattenToTree({ list: arr, rootId: 0 })))
/* 法二:不用递归,先把数据转成 Map 存储,之后遍历的同时借用 对象的引用,直接从 Map 中找到对应的数据做存储 *//* 两次循环,时间复杂度 O(2n),需要 Map 把数据存储起来,空间复杂度 O(n) *//* 此方法会过滤掉当个无关联节点,只是将关联节点进行了构建。如:{id: 8, name: '部门4', parentId: 90} id/parentId 都与其他节点无关联 */function FlattenToTree_01({ list, rootId }: { list: any[]; rootId: number }) { const result: any[] = []; let tempMap: any = {}; // 作用:将所有可能出现的节点汇总,方便按层级进行挂载 for (const item of list) { tempMap[item.id] = { ...item, children: [] }; } for (const iterator of list) { const id = iterator.id; const parentId = iterator.parentId; const treeItem = tempMap[id]; // 重要,当找到根节点时要 push 到 result 中 if (parentId === rootId) { result.push(treeItem); } else { // 做这一步判断的目的是什么??? if (!tempMap[parentId]) { tempMap[parentId] = { children: [], }; } tempMap[parentId].children.push(treeItem); } } return result;}// console.log(JSON.stringify(FlattenToTree_01({ list: arr, rootId: 0 })))
/* 法三:不用递归。 思路: 数据转成 Map 存储,之后遍历的同时借助 对象的引用,直接从 Map 中找到对应的数据做存储。 与方法二不同点在于,遍历时即做 Map 存储,*/function FlattenToTree_02({ list, rootId }: { list: any[]; rootId: number }) { const result: any[] = []; let tempMap: any = {}; // 作用:将所有可能出现的节点汇总,方便按层级进行挂载 for (const item of list) { const id = item.id; const parentId = item.parentId; if (!tempMap[id]) { tempMap[id] = { children: [], }; } tempMap[id] = { ...item, children: tempMap[id]["children"], }; const treeItem = tempMap[id]; if (parentId === rootId) { result.push(treeItem); } else { if (!tempMap[parentId]) { tempMap[parentId] = { children: [], }; } tempMap[parentId].children.push(treeItem); } } return result;}// console.log(JSON.stringify(FlattenToTree_02({ list: arr, rootId: 0 })))
const otherParams = { idName: "id", parentName: "parentId", childName: "children",};interface IGenerateTree { list: any[]; rootId: number | string; params?: typeof otherParams;}function generateTree({ list, rootId, params = otherParams }: IGenerateTree) { const { idName, parentName, childName } = params; const result: any[] = []; // 结果 let tempMap: any = {}; // 暂存数据,以 id 为 key 的映射关系 for (const item of list) { const id = item[idName]; const parentId = item[parentName]; if (!tempMap[id]) { tempMap[id] = { [childName]: [], }; } tempMap[id] = { ...item, [childName]: tempMap[id][childName], }; const treeItem = tempMap[id]; // 找到映射关系哪一项(注意这里是引用) if (parentId === rootId) { // 已经找到根元素,将映射结果放进结果集中 result.push(treeItem); } else { if (!tempMap[parentId]) { tempMap[parentId][childName] = []; } tempMap[parentId][childName].push(treeItem); } } return result;}console.log(JSON.stringify(generateTree({ list: arr, rootId: 0 })));