filterTree【过滤树形结构】🔥🔥🔥
描述
过滤递归遍历 Tree(树状结构),支持函数式编程(函数回调),高扩展 常用 v3.0.10+
// filter 为每个节点增加属性如下,__depth__、__id__、__level__、__pId__、__rootNode__
// children: (3) [{…}, {…}, {…}]
// id: 1
// name: "二级 1-1"
// order: 2
// parentId: null
// __depth__: 1 // 当前节点深度
// __id__: "0-1" // 节点经过的路径
// __level__: 1 // 当前节点深度
// __pId__: "0" // 父节点经过的路径
// __rootNode__: {id: 1, name: '二级 1-1', parentId: null, order: 2, children: Array(3), …} // 顶层根节点
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 1.示例
[
{
"id": 1,
"label": "一级 76666",
"children": [
{
"id": 4,
"label": "二级 1-1",
"children": [
{
"id": 9,
"label": "三级 1-1-1"
},
{
"id": 10,
"label": "三级 1-1-2"
}
]
}
]
},
{
"id": 2,
"label": "一级 2",
"children": [
{
"id": 5,
"label": "二级 2-1"
},
{
"id": 6,
"label": "二级 2-2"
}
]
},
{
"id": 3,
"label": "一级 3",
"children": [
{
"id": 7,
"label": "二级 3-1"
},
{
"id": 8,
"label": "二级 3-2"
}
]
}
] 复制代码
⚠️ 注意
在使用filterTree方法时,会默认给每个节点加上 __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__(当前节点相邻的下一个节点)属性。
# 2.入参说明
# 主入参
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| tree | 原数据(要转换的数据集) | Array | 是 | |
| props | 自定义字段键名,详情见下 👇 | Object | 否 | |
| callbackList | 每次列表循环回调函数,详情见下 👇 | Function | 否 | null |
| callbackItem | 每次列表每一项回调函数,详情见下 👇 | Function | 否 | null |
| isDeepClone | 是否要深拷贝原 tree 对象,默认true | Boolean | 否 | true |
# props 对象
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| children | 定义children键名 | String | 否 | children |
| order | 是否转化树形时排序 | Boolean | 否 | false |
| orderField | 定义排序键名 | String | 否 | sort |
| orderBy | 定义排序方向键名,可选值 asc(升序) desc(降序) | String | 否 | asc |
# callbackList 回调函数,接收参数,每一层树list 回调函数,方便排序
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| list | 当前树节点数组 | Array | 否 | |
| parentObj | 父节点对象 | Object | 否 |
# callbackItem 回调函数,接收参数
| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| item | 当前树节点 | Array | 否 | |
| index | 当前树节点索引 | Number | 否 | |
| list | 当前树节点数组 | Array | 否 | |
| parentObj | 父节点对象 | Array | 否 |
# 3.源码
import eachTree from '@/array/eachTree'
import arrayToObj from '@/array/arrayToObj'
import isFunction from '@/base/isFunction'
import uniq from '@/array/uniq'
import listToTree from '@/array/listToTree'
import isPlainObject from '@/base/isPlainObject'
import def from '@/object/def'
import deepClone from '@/object/deepClone'
import { _includesChildPath, _getPathLists } from '@/_helper/_helperTreeBase'
import merge from '@/object/merge'
/**
* 根据回调函数过滤树状
* @param {Array<any>} tree 树状结构
* @param {Object} props 属性映射
* @param {Boolean} retainChild 是否保留子孙节点
* @param {Function} callbackList 每一层树list 回调函数
* @param {Function} callbackItem 每一项回调函数
* @param {Boolean} isDeepClone 是否深度克隆原树型对象
* @returns {*}
*/
function filterTree({
tree = [],
props = { children: 'children', order: false, orderField: 'order', orderBy: 'asc' },
retainChild = false,
isDeepClone = true,
callbackItem,
callbackList
}) {
if (!isFunction(callbackItem)) return tree
if (isDeepClone) tree = deepClone(tree)
let defaultOptions = {
children: 'children',
order: false,
orderField: 'order',
orderBy: 'asc'
}
props = merge({}, defaultOptions, props)
const __id__ = '__id__'
const __pId__ = '__pId__'
const treeList = []
let validIdsList = []
eachTree({
tree: tree,
isDeepClone: false,
props: { children: props.children },
callbackItem: function (item, index, list, parentObj) {
treeList.push(item)
if (callbackItem(...arguments)) {
validIdsList.push(item[__id__])
}
}
})
const treeListObj = arrayToObj(treeList, __id__)
validIdsList = uniq(validIdsList) // 去重
const validIdsListObj = {}
validIdsList.forEach(v => {
const pathLists = _getPathLists(v)
pathLists.forEach(o => {
const target = treeListObj[o]
if (target) {
validIdsListObj[o] = target
}
})
if (retainChild) {
treeList
.filter(o => _includesChildPath(o[__id__], v))
.forEach(o => {
const key = o[__id__]
validIdsListObj[key] = o
// console.log('existIdx@', key, v)
})
}
})
const __id2__ = '__@id__'
const __pId2__ = '__@pId__'
const list = Object.values(validIdsListObj).map(v => {
if (isPlainObject(v)) {
def(v, __id2__, v[__id__])
def(v, __pId2__, v[__pId__])
delete v[props.children]
}
return v
})
// console.log('Object.values(validIdsListObj)', list, Object.values(validIdsListObj))
// console.log('变化了@1')
return listToTree({
list,
root: '@',
props: { id: __id2__, parentId: __pId2__, children: props.children },
callbackList,
callbackItem: item => {
delete item[__id2__]
delete item[__pId2__]
},
isDeepClone: false
})
}
export default filterTree
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
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
上次更新: 2025/01/11, 15:37:45