eachTree【遍历树形结构】🔥🔥🔥
描述
递归遍历 Tree(树状结构),支持函数式编程(函数回调),高扩展常用
// eachTree 为每个节点增加属性如下,__depth__、__id__、__level__、__pId__、__rootNode__
// children: (3) [{…}, {…}, {…}]
// id: 1
// name: "香蕉"
// order: 2
// parentId: null
// __depth__: 1 // 当前节点深度
// __id__: "0-1" // 节点经过的路径
// __level__: 1 // 当前节点深度
// __parentNode__: {id: 1, name: '香蕉', parentId: null, order: 2, children: Array(3), …} // 当前父节点
// __pId__: "0" // 父节点经过的路径
// __rootNode__: {id: 1, name: '香蕉', parentId: null, order: 2, children: Array(3), …} // 顶层根节点
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
# 1.1 示例 根据 callbackFn 自定义格式化方法,排序
源码,点开查看 👈
let tree = [
{
id: 1,
name: '香蕉',
pId: null,
order: 10,
children: [
{
id: 1001,
name: '香蕉A',
pId: 1,
children: [
{
id: 1001001,
name: '香蕉A-儿子',
pId: 1001,
children: [
{
id: 1001001001,
name: '香蕉A-儿子-儿子',
pId: 1001001
}
]
}
]
},
{
id: 1002,
name: '香蕉B',
pId: 1,
order: 21,
children: [
{
id: 1002002,
name: '香蕉B-儿子',
pId: 1002
}
]
},
{
id: 1003,
name: '香蕉C',
pId: 1,
order: 3,
children: [
{
id: 1003003,
name: '香蕉C-儿子',
pId: 1003
}
]
}
]
},
{
id: 2,
name: '苹果',
pId: null,
order: 2,
children: [
{
id: 1004,
name: '苹果A',
pId: 2,
children: [
{
id: 1004004,
name: '苹果A-儿子',
pId: 1004
}
]
},
{
id: 1007,
name: '苹果D',
order: 5,
pId: 2
},
{
id: 1008,
name: '苹果D',
order: 15,
pId: 2
},
{
id: 1009,
name: '苹果C',
order: 10,
pId: 2
}
]
}
]
// 根据callbackFn自定义格式化方法,排序
console.log(
eachTree({
tree: tree,
props: { children: 'children' },
callbackList: (list, parentObj) => {
// list:每个节点下子节点数组 parentObj:父节点对象
console.log('回调函数@callbackList', list, parentObj)
list.sort((a, b) => a.order - b.order)
}
})
)
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
输出结果
⚠️ 注意
这边在eachTree方法时,会默认给每个节点加上 __id__(节点经过路径) 、 __pId__(父节点经过的路径)、__depth__(当前节点深度)、__rootNode__(最上层,根节点对象)
,__id__ 每一个子节点唯一值,__pId__ 每一个子节点中的父节点值,
你会发现这边的 __id__ 看起来是有规则的,它是下标索引为基值,比如 __id__ 是 0-1-1-1-1 时,表面它处于第 4 层、之前途径的路径是 0-1、0-1-1、
0-1-1-1, 这样方便平时业务中做其他操作,比如要获取每个子节点所有途经的节点集合,我们只需要遍历刚才的 0-1、0-1-1、
0-1-1-1 去 list 中找到 __id__ 是其中之一。是不是感觉很方便?😁
同时还会给每个节点默认加上__depth__(当前节点深度)、__rootNode__(最上层,根节点对象)
v3.3.0+ 每一个节点都会增加 __prevNode__(当前节点相邻的上一个节点)属性、__nextNode__(当前节点相邻的下一个节点)属性。
# 1.2 示例 根据 callbackList 和 callbackItem 回调函数,输出结果
源码,点开查看 👈
import { eachTree } from 'sf-utils2'
let tree = [
{
id: 1,
name: '香蕉',
pId: null,
order: 10,
children: [
{
id: 1001,
name: '香蕉A',
pId: 1,
children: [
{
id: 1001001,
name: '香蕉A-儿子',
pId: 1001,
children: [
{
id: 1001001001,
name: '香蕉A-儿子-儿子',
pId: 1001001
}
]
}
]
},
{
id: 1002,
name: '香蕉B',
pId: 1,
order: 21,
children: [
{
id: 1002002,
name: '香蕉B-儿子',
pId: 1002
}
]
},
{
id: 1003,
name: '香蕉C',
pId: 1,
order: 3,
children: [
{
id: 1003003,
name: '香蕉C-儿子',
pId: 1003
}
]
}
]
},
{
id: 2,
name: '苹果',
pId: null,
order: 2,
children: [
{
id: 1004,
name: '苹果A',
pId: 2,
children: [
{
id: 1004004,
name: '苹果A-儿子',
pId: 1004
}
]
},
{
id: 1007,
name: '苹果D',
order: 5,
pId: 2
},
{
id: 1008,
name: '苹果D',
order: 15,
pId: 2
},
{
id: 1009,
name: '苹果C',
order: 10,
pId: 2
}
]
}
]
// 根据callbackFn自定义格式化方法,排序
eachTree({
tree: tree,
props: { children: 'children' },
callbackList: (list, parentObj) => {
// list:每个节点下子节点数组 parentObj:父节点对象
console.log('回调函数的值@callbackList', list, parentObj)
list.sort((a, b) => a.order - b.order)
// 返回true时,表示告诉 eachTree 终止调用[callbackList]循环,即不再调用该callbackList方法
},
callbackItem: (item, index, list, parentObj) => {
// item => 当前树节点, index => 当前树节点索引, list => 当前树节点数组, parentObj => 父节点
console.log('回调函数的值@callbackItem', item, index, list, parentObj)
// 其中item输出的值是:
// children: (3) [{…}, {…}, {…}]
// id: 1
// name: "香蕉"
// order: 2
// parentId: null
// __depth__: 1 // 当前节点深度
// __id__: "0-1" // 节点经过的路径
// __level__: 1 // 当前节点深度
// __pId__: "0" // 父节点经过的路径
// __parentNode__: {id: 1, name: '香蕉', parentId: null, order: 2, children: Array(3), …} // 当前父节点
// __rootNode__: {id: 1, name: '香蕉', parentId: null, order: 2, children: Array(3), …} // 顶层根节点
return true // 返回true时,表示告诉 eachTree 终止当前节点循环,不再进行其子孙节点循环,但其兄弟节点还是要走[callbackItem]逻辑循环。
}
})
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
输出结果
# 1.3 示例 先子节点到父节点 递归遍历【莫发达提供】
注:以前我没注意 都是从父到子进行递归,先子节点到父节点 递归遍历【
莫发达提供】,默认是先父节点到子节点。
源码,点开查看 👈
import { eachTree } from 'sf-utils2'
let tree = [
{
id: 1,
name: '香蕉',
pId: null,
order: 10,
children: [
{
id: 1001,
name: '香蕉A',
pId: 1,
children: [
{
id: 1001001,
name: '香蕉A-儿子',
pId: 1001,
children: [
{
id: 1001001001,
name: '香蕉A-儿子-儿子',
pId: 1001001
}
]
}
]
},
{
id: 1002,
name: '香蕉B',
pId: 1,
order: 21,
children: [
{
id: 1002002,
name: '香蕉B-儿子',
pId: 1002
}
]
},
{
id: 1003,
name: '香蕉C',
pId: 1,
order: 3,
children: [
{
id: 1003003,
name: '香蕉C-儿子',
pId: 1003
}
]
}
]
},
{
id: 2,
name: '苹果',
pId: null,
order: 2,
children: [
{
id: 1004,
name: '苹果A',
pId: 2,
children: [
{
id: 1004004,
name: '苹果A-儿子',
pId: 1004
}
]
},
{
id: 1007,
name: '苹果D',
order: 5,
pId: 2
},
{
id: 1008,
name: '苹果D',
order: 15,
pId: 2
},
{
id: 1009,
name: '苹果C',
order: 10,
pId: 2
}
]
}
]
// 根据callbackFn自定义格式化方法,排序
eachTree({
tree: tree,
props: { children: 'children' },
traversalOrder: 'postorder',
callbackItem: (item, index, list, parentObj) => {
// item:当前节点对象 parentObj:父节点对象
console.log('回调函数@callbackItem', item, index, list, parentObj)
}
})
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
输出结果
# 2.入参说明
# 主入参
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| tree | 原数据(要转换的数据集) | Array | 是 | |
| props | 自定义字段键名,详情见下 👇 | Object | 否 | |
| callbackList | 每次列表循环回调函数,详情见下 👇 | Function | 否 | null |
| callbackItem | 每次列表每一项回调函数,详情见下 👇 | Function | 否 | null |
| isDeepClone | 是否要深拷贝原 tree 对象,默认true | Boolean | 否 | true |
| retainField | 每条数据上要保留的字段 | ('__rootNode__', '__pId__', '__level__')[] | 否 | [] |
| traversalOrder | 树的遍历顺序 v3.2.4+,感谢莫发达童鞋提出意见 | preorder(先进行父->子 默认)、postorder(后进行子->父) | 否 | preorder |
# props 对象
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| children | 定义children键名 | String | 否 | children |
| order | 是否转化树形时排序 | Boolean | 否 | false |
| orderField | 定义排序键名 | String | 否 | sort |
| orderBy | 定义排序方向键名,可选值 asc(升序) desc(降序) | String | 否 | asc |
# callbackList 回调函数,接收参数
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| list | 当前树节点数组 | Array | 否 | |
| parentObj | 父节点对象 | Object | 否 |
# callbackItem 回调函数,接收参数
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| item | 当前树节点 | Array | 否 | |
| index | 当前树节点索引 | Number | 否 | |
| list | 当前树节点数组 | Array | 否 | |
| parentObj | 父节点对象 | Array | 否 |
# 3.源码
import merge from '@/object/merge'
import _helperTreeBase from '../_helper/_helperTreeBase.js'
/**
* 从树结构中遍历
* @param tree
* @param callbackFn
*/
function eachTree({
tree = [],
props = { children: 'children', order: false, orderField: 'order', orderBy: 'asc' },
callbackList,
callbackItem,
isDeepClone = true
}) {
let defaultOptions = {
children: 'children',
order: false,
orderField: 'order',
orderBy: 'asc'
}
props = merge({}, defaultOptions, props)
return _helperTreeBase({ props, callbackList, callbackItem, tree, isDeepClone })
}
export default eachTree
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
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
上次更新: 2025/01/11, 15:37:45