树形结构转列表
/*
题目描述:
[
{
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 })));