树表互转

树转表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function treeData2tableData(td = []) {
return td.reduce(
(acc, { id, pid, children, label }) =>
[...acc, ...treeData2tableData(children), { id, pid, label }],
[]
);
}

let treeData = [
{
id: '1', pid: null, label: '1',
children: [
{
id: '1-1', pid: '1', label: '1-1',
children: [
{
id: '1-1-1', pid: '1-1', label: '1-1-1',
}
]
},
{
id: '1-2', pid: '1', label: '1-2'
}
]
},
{
id: '2', pid: null, label: '2',
children: [
{
id: '2-1', pid: '2', label: '2-1'
},
{
id: '2-2', pid: '2', label: '2-2'
}
]
}
]
let cc = treeData2tableData(treeData)
console.log(cc)

分析

  1. 首选设置函数参数的默认值,这是为了解决 children 为空的情况

  2. 结构必要的 key 值, 进行循环 children

  3. 递归分析 :

    1. children 进行函数执行,初始值还是为 [], 所以解构为
      1. id: ‘1-1’, pid: ‘1’, label: ‘1-1’, children: []
    2. 继续执行 …treeData2tableData(children) 函数
    3. 解构为 id: ‘1-1-1’, pid: ‘1-1’, label: ‘1-1-1’ …
    4. 继续执行 …treeData2tableData(children) , 因为为 undefined, 所以用 [] 进行执行函数
    5. 回到堆地址,继续执行 解构 [{id, pid, label}]
    6. 输出 [{id: ‘1-1-1’, pid: ‘1-1’, label: ‘1-1-1’}]
  4. 继续执行 reduce 函数, 因为上一次返回的是 [{id: ‘1-1-1’, pid: ‘1-1’, label: ‘1-1-1’}],将其压入堆,找到上一次起始点

    1. 找到上一次起始点为: {id: ‘1-1’, pid: ‘1’, label: ‘1-1’}, 根据 reduce 的特性, acc 的值为 :
      [{id: ‘1-1-1’, pid: ‘1-1’, label: ‘1-1-1’},{id: ‘1-1’, pid: ‘1’, label: ‘1-1’}]

    2. 所以下一个要执行的对象就为 {id: ‘1-2’, pid: ‘1’, label: ‘1-2’}了, 执行.. 解构 …, 数组为空…

    3. return 解构后的内容:
      [{id: “1-1-1”, pid: “1-1”, label: “1-1-1”},{id: “1-1”, pid: “1”, label: “1-1”},{id: “1-2”, pid: “1”, label: “1-2”}]

    4. 继续 执行 acc …..

难点

  • 首先需要 了解 reduce 的特性, 将参数里的内容依次执行一遍函数,如果传入初始值,那么 索引将从 0 开始,否则从 1 开始
  • 注意处理 为空 的情况, 当参数为空时,进行堆地址回归, 返回 reduce 计算

表转树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
function tableData2treeData(td = []) {
const cache = new Map()
const rest = [...td]
const ret = []
do {
const originItem = rest.shift()
const { id, pid, ...restProps } = originItem
const item = {id, pid, ...restProps}
const target = cache.get(pid)
if(pid === null) {
ret.push(item)
} else if (target){
(target.children || (target.children = [])).push(item)
} else {
rest.push(item)
}
cache.set(id, item);
} while (rest.length);
return ret
}

let tableData = [
{id: '1', pid: null, label: '1'},
{id: '1-1', pid: '1', label: '1-1'},
{id: '1-1-1', pid: '1-1', label: '1-1-1'},
{id: '1-2', pid: '1', label: '1-2'},
{id: '2', pid: null, label: '2'},
{id: '2-1', pid: '2', label: '2-1'},
{id: '2-2', pid: '2', label: '2-2'}
]

let test = tableData2treeData(tableData)
console.log(test)

分析

  1. 首先观察数据结构:

    1.1 顶级元素的 id为 1 , 2, pid 为 null

    1.2 子级的 pid 为 父级的 id

    1.3 孙级的 pid 为 子级的 id

    1.4 整个数组为 顶级元素一整个对象,里面 children 无限嵌套

  2. 函数分析

    2.1 这里使用 Map 对象 Map对象与 Object 类似,可自由选择 : Map 在涉及频繁增删键值对的场景下会有些性能优势

    2.2 如果 pid 为顶级元素 null, 则直接 push 进行 ret 里

    2.3 如果 子级的 pid === 父级 id, 判断是否存在 children,否则定义 [] 进行 push

    2.4 循环拿取 判断 push 进不同的 item 里面

    2.5 返回 ret

难点

  • 学习使用 Map 对象

文章目录
  1. 1. 树转表
    1. 1.1. 分析
      1. 1.1.1. 难点
  2. 2. 表转树
    1. 2.1. 分析
      1. 2.1.1. 难点
|