# 扁平化与树形结构互转
扁平化数据
let arr = [
{id: 1, name: '1', pid: 0},
{id: 2, name: '2', pid: 1},
{id: 3, name: '3', pid: 1},
{id: 4, name: '4', pid: 3},
{id: 5, name: '5', pid: 3},
]
1
2
3
4
5
6
7
2
3
4
5
6
7
树形数据
let tree = [
{
"id": 1,
"name": "1",
"pid": 0,
"children": [
{
"id": 2,
"name": "2",
"pid": 1,
"children": []
},
{
"id": 3,
"name": "3",
"pid": 1,
"children": [
{
"id": 4,
"name": "4",
"pid": 3,
"children": []
},
{
"id": 5,
"name": "5",
"pid": 3,
"children": []
}
]
}
]
}
]
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
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
# 树形数据->扁平化数据
1. 方法1 普通递归
function transTreeToPlain(tree) {
let res = []
for (const item of tree) {
const { children, ...i } = item
if (children && children.length) {
res = res.concat(transTreeToPlain(children))
}
res.push(i)
}
return res
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
2. reduce实现
function transTreeToPlain(tree) {
return tree.reduce((res, item) => {
const { children, ...i } = item
return res.concat(i, children && children.length ? transTreeToPlain(children) : [])
}, [])
}
1
2
3
4
5
6
2
3
4
5
6
# 扁平化数组转tree
1. 递归
function transferPlainToTree(items) {
let res = []
let getChildren = (res, pid) => {
for (const i of items) {
if (i.pid === pid) {
const newItem = { ...i, children: [] }
res.push(newItem)
getChildren(newItem.children, newItem.id)
}
}
}
getChildren(res, 0)
return res
}
该算法的时间复杂度为O(2^n)。性能消耗很大
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2. map对象实现
思路:先把数据转成Map去存储,然后再遍历的同时借助对象的引用,直接从Map找对应的数据做存储。
Object.prototype.hasOwnProperty: 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性,会忽略掉那些从原型链上继承到的属性。
function transferPlainToTree(items) {
let res = [] // 存放结果集
let map = {}
// 先转成map存储
for (const i of items) {
map[i.id] = { ...i, children: [] }
}
for (const i of items) {
const newItem = map[i.id]
if (i.pid === 0) {
//缓存最外层root节点
res.push(newItem)
} else {
if (Object.prototype.hasOwnProperty.call(map, i.pid)) {
map[i.pid].children.push(newItem)
}
}
}
return res
}
有两次循环,时间复杂度为O(2n),需要一个Map把数据存储起来,空间复杂度O(n)
当然,我们也可以边做map存储,边找对应关系,一次循环搞定。
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
3. 收集子元素
/**
* 扁平化处理数据
* 思路:遍历每一个item 然后递归子项是否存在
* @param {*} data 初始化数据
*/
function transferPlainToTree(data) {
//首先对原数据进行深拷贝
const _data = JSON.parse(JSON.stringify(data));
return _data.filter((p) => {
//每次遍历都会遍历获取子级
const _arr = _data.filter((c) => c.pid === p.id);
//根据_arr来判断是否有子级
_arr.length && (p.children = _arr);
//最后获取pid === 0的数据
return p.pid === 0;
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
4. 边做map存储,边找对应关系
思路:循环将该项的id为键,存储到map中,如果已经有该键值对了,则不用存储了,同时找该项的pid在不在map的键中,在直接对应父子关系,不在就在map中生成一个键值对,键为该pid,然后再对应父子关系。
function transferPlainToTree(items) {
let res = [] // 存放结果集
let map = {}
// 判断对象是否有某个属性
let getHasOwnProperty = (obj, property) => Object.prototype.hasOwnProperty.call(obj, property)
// 边做map存储,边找对应关系
for (const i of items) {
map[i.id] = {
...i,
children: getHasOwnProperty(map, i.id) ? map[i.id].children : []
}
const newItem = map[i.id]
if (i.pid === 0) {
res.push(newItem)
} else {
if (!getHasOwnProperty(map, i.pid)) {
map[i.pid] = {
children: []
}
}
map[i.pid].children.push(newItem)
}
}
return res
}
一次循环就搞定了,性能也很好。时间复杂度为O(n),需要一个Map把数据存储起来,空间复杂度O(n)
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
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