JS递归过滤树形结构数组对象--模糊查询

📝 个人简介

⭐ 个人主页:我是段段🙋‍
🍊 博客领域:编程基础、前端💻
🍅 写作风格:干货!干货!都是干货!
🍑 精选专栏: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>

以后就是筛选树形数据的整个过程,有看不懂的地方欢迎留言讨论

如果觉得有帮助给博主点个赞和关注~~

  • 17
    点赞
  • 49
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我是段段

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值