Angular/TypeScript实现无限层级对象的名称过滤需求
无限层级对象数组的名称过滤解决方案
需求说明
处理无限层级的对象数组(每个对象包含id、name、children属性),实现输入监听功能按name属性过滤:
- 返回所有名称匹配搜索词的对象
- 保留自身名称不匹配但子孙节点名称匹配的父对象
- 保留的父对象仅包含其下匹配的子孙节点
现有问题
当前代码仅支持父级和直接子级的过滤逻辑,无法处理深层嵌套的场景。
示例数据
原示例数组:
[原示例数组]
输入"aaa"时预期返回:
[预期结果数组]
现有代码
this.groupNameSearch.valueChanges.subscribe((value) => { const searchedPhrase = value.toLowerCase(); this.groups = _.cloneDeep(this.allGroups).filter((group) => { const matchedGroup = group.name.toLowerCase().includes(searchedPhrase); group.children = group.children.filter((child) => child.name?.toLowerCase().includes(searchedPhrase)); return matchedGroup || group.children.length; }); });
解决方案
核心思路是通过递归遍历处理每一层节点,实现深层嵌套的过滤逻辑。
递归过滤函数
先实现一个递归函数,负责处理单一层级的数组,并递归处理子节点:
function filterGroups(groups: any[], searchPhrase: string): any[] { const lowerPhrase = searchPhrase.toLowerCase(); return groups.reduce((result, currentGroup) => { // 递归处理当前节点的子节点,得到过滤后的子节点列表 const filteredChildren = filterGroups(currentGroup.children || [], lowerPhrase); // 检查当前节点名称是否匹配搜索词 const isCurrentMatch = currentGroup.name.toLowerCase().includes(lowerPhrase); // 若当前节点匹配,或存在匹配的子孙节点,则保留该节点 if (isCurrentMatch || filteredChildren.length > 0) { result.push({ ...currentGroup, children: filteredChildren // 替换为过滤后的子节点 }); } return result; }, []); }
替换原有监听逻辑
将原来的订阅逻辑替换为调用递归函数:
this.groupNameSearch.valueChanges.subscribe((value) => { const searchedPhrase = value.toLowerCase(); // 深拷贝原数组避免修改源数据,再传入递归函数过滤 this.groups = filterGroups(_.cloneDeep(this.allGroups), searchedPhrase); });
逻辑说明
- 递归遍历每个节点,先处理其所有子节点,确保深层的匹配节点被过滤出来
- 对每个节点判断是否需要保留:自身名称匹配,或者有经过滤后的子节点(说明存在匹配的子孙)
- 保留的节点会携带过滤后的子节点列表,只保留匹配的分支路径
内容的提问来源于stack exchange,提问作者Maciej Grzegorczyk
相关产品推荐
相关产品推荐

