📝 个人简介
⭐ 个人主页:我是段段🙋
🍊 博客领域:编程基础、前端💻
🍅 写作风格:干货!干货!都是干货!
🍑 精选专栏:JavaScript
🛸 支持段段:点赞👍、收藏⭐、留言💬
前言
2022年第一篇文章,祝大家新年快乐呀~
在工作中为了优化项目性能,决定由前端来实现简单树形数据的搜索功能,有一个功能需求是这样
的,简单描述下需求内容
1、父节点含有,将父节点返回(包含所有子节点)
2、父节点没有,但子节点含有,父节点仍要返回,而子节点只返回包含的搜索内容的
自己也写了算法来实现,可是总有点问题,于是就上网查了,果然有好多方法,但是有些方法是具有局限性的,因为不能确定树形结构的层级是多深的,所以还是边查边修改,调试了好长时间,最终找到了一个比较好的解决方
法,并在项目项目中进行了测试,简单写点东西记录下
方式一、方式二是有局限性的,方式三是比较好的解决方法
方式一 filter + 递归(需要有标识字段)
缺陷:只能一级接着一级的进行筛选,不能实现第二个需求
简单描述下实现的过程,首先是用到的测试数据
const { log } = console
let arr = [
{
id: 1,
isShow: false,
child: [
{
id: 2,
isShow: true,
child: []
}
]
},
{
id: 3,
isShow: true,
child: [
{
id: 4,
isShow: true,
child: []
},
{
id: 5,
isShow: false,
child: []
}
]
}
]
然后编写两个函数来处理数据
let detailChild = (child) => {
if (!child) return []
return child.filter((item) => {
if (item.isShow) {
item.child = detailChild(item.child)
}
return item.isShow
})
}
let result = arr.filter((item) => {
// 在遍历的时候判断了isShow的状态 直接过滤掉了父节点为false而子节点为true的情况
if (item.isShow && item.child && item.child.length) {
item.child = detailChild(item.child)
}
return item.isShow
})
// 筛选结果在result中,打印result,查看结果
log(result)
筛选的结果如下
可以看到结果中只有id为3 和 4
的,但是arr中共有两个对象,第一个对象即是父节点isShow为fasle,子节点
isShow为true
,而打印结果中却无法显示,即处理数据有局限性
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>方式一</title>
</head>
<body>
<script>
const { log } = console
let arr = [
{
id: 1,
isShow: false,
child: [
{
id: 2,
isShow: true,
child: []
}
]
},
{
id: 3,
isShow: true,
child: [
{
id: 4,
isShow: true,
child: []
},
{
id: 5,
isShow: false,
child: []
}
]
}
]
let detailChild = (child) => {
if (!child) return []
return child.filter((item) => {
if (item.isShow) {
item.child = detailChild(item.child)
}
return item.isShow
})
}
let result = arr.filter((item) => {
// 在遍历的时候判断了isShow的状态 直接过滤掉了父节点为false而子节点为true的情况
if (item.isShow && item.child && item.child.length) {
item.child = detailChild(item.child)
}
return item.isShow
})
log(result)
</script>
</body>
</html>
方式二 indexOf + filter + 递归
缺陷:同样只能一级接着一级的进行筛选,不能实现第二个需求
简单描述下实现的过程,首先是用到的测试数据
const { log } = console
// 菜单列表
const arr = [
{
name: '系统管理',
code: 'system_manage',
children: [
{
name: '用户管理',
code: 'user_manage',
children: [
{
name: '添加用户',
code: 'add_user',
children: []
},
{
name: '编辑用户',
code: 'edit_user',
children: []
},
{
name: '删除用户',
code: 'del_user',
children: []
}
]
},
{
name: '角色管理',
code: 'role_manage',
children: [
{
name: '添加角色',
code: 'add_role',
children: []
}
]
}]
},
{
name: '业务管理',
code: 'bus_manage',
children: [
{
name: '流程管理',
code: 'process_manage',
children: []
}
]
},
{
name: '订单管理',
code: 'order_manage',
children: []
}
]
然后将需要筛选的字段放在一个数组中
// 权限列表
let menuCode = ['system_manage', 'user_manage', 'add_user', 'order_manage']
然后编写一个函数用来处理数据
let filterMenu = (list, Code) => {
return list.filter(item => {
return Code.indexOf(item.code) > -1
}).map(item => {
item = Object.assign({}, item)
if (item.children) {
item.children = filterMenu(item.children, Code)
}
return item
})
}
// 过滤后的菜单
const menu = filterMenu(arr, menuCode)
log(menu)
筛选的结果如下
从结果可以看出,数组中需要筛选的字段确实都筛选出来了,但是数组中前三个字段是一层接着一层的,如下图
不妨尝试去掉前两个字段(system_manage 和 user_manage
),此时筛选条件变为
// 权限列表
let menuCode = ['add_user', 'order_manage']
然后查看打印结果
从结果中可以看出没有add_user
字段的结果信息,因为此时没有了父级字段的筛选
,所以子节点无法查询,即处理数据有局限性
完整代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document2</title>
</head>
<body>
<script>
const { log } = console
// 菜单列表
const arr = [
{
name: '系统管理',
code: 'system_manage',
children: [
{
name: '用户管理',
code: 'user_manage',
children: [
{
name: '添加用户',
code: 'add_user',
children: []
},
{
name: '编辑用户',
code: 'edit_user',
children: []
},
{
name: '删除用户',
code: 'del_user',
children: []
}
]
},
{
name: '角色管理',
code: 'role_manage',
children: [
{
name: '添加角色',
code: 'add_role',
children: []
}
]
}]
},
{
name: '业务管理',
code: 'bus_manage',
children: [
{
name: '流程管理',
code: 'process_manage',
children: []
}
]
},
{
name: '订单管理',
code: 'order_manage',
children: []
}
]
// 权限列表
let menuCode = ['add_user', 'order_manage']
let filterMenu = (list, Code) => {
return list.filter(item => {
return Code.indexOf(item.code) > -1
}).map(item => {
item = Object.assign({}, item)
if (item.children) {
item.children = filterMenu(item.children, Code)
}
return item
})
}
// 过滤后的菜单
const menu = filterMenu(arr, menuCode)
log(menu)
</script>
</body>
</html>
方式三 includes + 递归
尝试了好多方法,此方法处理树形结构是比较好的
简单描述下实现的过程,首先是用到的测试数据
const { log } = console
let arr = [
{
title: '你好吗?',
children: [
{
title: '很好啊',
children: []
},
{
title: '是吗',
children: []
}
]
},
{
title: '卡卡卡',
children: [
{
title: '非常好芬',
children: []
}
]
},
{
title: '第三方的好',
children: []
}
]
然后编写一个函数用来处理数据
let onRecursionData = (arr, val) => {
let newarr = []
arr.forEach(item => {
if (item.children && item.children.length) {
let children = onRecursionData(item.children, val)
let obj = {
...item,
children
}
if (children && children.length) {
newarr.push(obj)
}
} else {
if (item.title.includes(val)) {
newarr.push(item)
}
}
})
return newarr
}
let result = onRecursionData(arr, '好')
log(result) // 打印结果
查看输出结果
从结果中可以看出,arr中有三个对象,第一个对象是父节点和子节点都包含匹配的字段
,并且都成功返回了,第二个对象是父节点不包含匹配的字段,而子节点包含匹配的字段
,第三个对象是父节点包含匹配的字段,无子节点
补充补充:
在使用的过程中,发现以上代码存在一个缺陷,即第四种:父节点包含匹配的字段而子节点不包含匹配的字段的情况
无法筛选出来,例如用以下数据进行测试
let arr = [
{
title: '你吗?',
children: [
{
title: '很好啊',
children: []
},
{
title: '吗',
children: [
{
title: '好呀',
children: []
}
]
}
]
},
{
title: '卡卡卡',
children: [
{
title: '非常好芬',
children: []
}
]
},
{
title: '好卡',
children: [
{
title: '非常芬',
children: []
}
]
},
{
title: '第三方的好',
children: []
},
{
title: '第三方的',
children: [
{
title: '的',
children: []
}
]
}
]
结果如下
针对这组数据用原来的方法
进行测试,无法测试出来好卡 非常芬
这组数据
然后又重新阅读代码,对原封装的函数进行修改,修改后的代码如下
let onRecursionData = (arr, val) => {
let newarr = []
arr.forEach(item => {
if (item.children && item.children.length) {
let children = onRecursionData(item.children, val)
let obj = {
...item,
children
}
if (children && children.length) {
newarr.push(obj)
} else if(item.title.includes(val)){
newarr.push({ ...item })
}
} else {
if (item.title.includes(val)) {
newarr.push(item)
}
}
})
return newarr
}
修改后的测试效果如下
可以看到下标为2
的数据就是刚才没筛选出来的数据
完整代码如下
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
const { log } = console
let arr = [
{
title: '你吗?',
children: [
{
title: '很好啊',
children: []
},
{
title: '吗',
children: [
{
title: '好呀',
children: []
}
]
}
]
},
{
title: '卡卡卡',
children: [
{
title: '非常好芬',
children: []
}
]
},
{
title: '好卡',
children: [
{
title: '非常芬',
children: []
}
]
},
{
title: '第三方的好',
children: []
},
{
title: '第三方的',
children: [
{
title: '的',
children: []
}
]
}
]
let onRecursionData = (arr, val) => {
let newarr = []
arr.forEach(item => {
if (item.children && item.children.length) {
let children = onRecursionData(item.children, val)
let obj = {
...item,
children
}
if (children && children.length) {
newarr.push(obj)
} else if(item.title.includes(val)){
newarr.push({ ...item })
}
} else {
if (item.title.includes(val)) {
newarr.push(item)
}
}
})
return newarr
}
let result = onRecursionData(arr, '好')
log(result)
</script>
</body>
</html>
以后就是筛选树形数据的整个过程,有看不懂的地方欢迎留言讨论
如果觉得有帮助给博主点个赞和关注~~