新闻中心
使用递归过滤深度嵌套对象数组并保留父级层级

本文旨在解决在过滤深度嵌套的对象数组时,如何保留匹配项的父级层级结构的问题。通过将数据结构统一为使用`children`键表示子项,并采用自定义的递归过滤函数,可以有效地实现这一目标,确保仅包含符合条件的子项及其所有祖先节点,同时保持数据结构的完整性。
在处理复杂的、多层级的数据结构(如产品目录、组织架构等)时,我们经常需要根据特定条件对数据进行深度过滤。一个常见的挑战是,当子节点满足过滤条件时,如何确保其所有父节点也被保留在结果中,即使这些父节点本身不直接满足过滤条件。传统的深度过滤库,如deepdash的_.filterDeep,在默认情况下可能会移除不匹配的父节点,导致结果丢失上下文信息。
深度嵌套数据过滤的挑战
考虑一个包含多层产品分类的数组,每个产品都有唯一的code。我们的目标是根据code进行搜索,并返回所有匹配的产品及其完整的祖先路径(从顶层分类到产品本身)。
原始数据结构可能如下所示:
const products_array = [
{
name: 'Food to Go',
filter: 'food',
categories_list: [ // 第一层子项
{
name: 'Bepulp Compostable',
sub_categories: [ // 第二层子项
{
name: 'BOWLS & CONTAINERS',
products: [ // 第三层子项
{
type: 'RECTANGULAR',
products_list: [ // 第四层子项 (实际产品)
{ color: 'natural', code: 'PAP46120', description: 'Rectangular tray 600ml' },
{ color: 'natural', code: 'PUL46130', description: 'Rectangular tray 950ml' },
// ...更多产品
]
},
// ...更多类型
]
},
// ...更多子分类
],
},
// ...更多分类
],
},
// ...更多顶级产品组
];如果使用 deepdash 的 _.filterDeep 并且只在叶子节点上匹配,结果可能会是这样的:
const current_output = [
{
categories_list: [
{
sub_categories: [
{
products: [
{
products_list: [
{ code: 'PUL46130' },
{ code: 'PUL51601' },
// ...
]
}
]
}
]
}
]
}
];可以看到,current_output 仅保留了匹配项及其直接父级的空壳,丢失了所有父节点的其他属性(如name, filter, color等),并且层级结构也变得不完整。
期望的输出
我们期望的输出是这样的:如果某个产品(例如 code: 'PUL46130')匹配,那么它的所有父级(RECTANGULAR 类型、BOWLS & CONTAINERS 子分类、Bepulp Compostable 分类、Food to Go 顶级组)都应该被完整地保留下来,包括它们的非子项属性。
const expected_output = [
{
name: 'Food to Go',
filter: 'food',
color: '#f9dd0a',
categories_list: [
{
name: 'Bepulp Compostable',
sub_categories: [
{
name: 'BOWLS & CONTAINERS',
products: [
{
type: 'RECTANGULAR',
products_list: [
{ color: 'natural', code: 'PUL46130', description: 'Rectangular tray 950ml' },
{ color: 'clear', code: 'PUL51601', description: 'rPET lid' },
// ...其他匹配产品
]
},
// ...其他匹配类型
]
},
// ...其他匹配子分类
],
},
// ...其他匹配分类
],
},
// ...其他匹配顶级产品组
];解决方案:统一子节点键与递归过滤
为了优雅地实现这种带有层级保留的深度过滤,一个有效的策略是统一数据结构中表示子节点的键名,例如全部使用children。然后,我们可以编写一个自定义的递归过滤函数。
察言观数AskTable
企业级AI数据表格智能体平台
78
查看详情
1. 数据结构预处理(如果需要)
如果你的原始数据结构像products_array那样,子节点键名不统一(categories_list, sub_categories, products, products_list),你需要先将其转换为统一使用children键的结构。例如:
const transformed_products = [
{
name: 'Food to Go',
filter: 'food',
color: '#f9dd0a',
children: [ // 原来的 categories_list
{
name: 'Bepulp Compostable',
children: [ // 原来的 sub_categories
{
name: 'BOWLS & CONTAINERS',
children: [ // 原来的 products
{
name: 'RECTANGULAR', // 或者 type 属性
children: [ // 原来的 products_list
{ name: 'PUL46120', color: 'natural', code: 'PUL46120', description: 'Rectangular tray 600ml' },
{ name: 'PUL46130', color: 'natural', code: 'PUL46130', description: 'Rectangular tray 950ml' },
// ...
]
},
// ...
]
},
// ...
],
},
// ...
],
},
// ...
];注意:此转换步骤本身不在本教程的过滤函数中实现,但它是使以下递归过滤函数简洁高效的前提。
2. 实现递归过滤函数
核心思路是:对于每个节点,首先检查它是否满足过滤条件。如果满足,则保留该节点。如果它不满足,但它的任何子节点满足过滤条件,那么也保留该节点(因为它是一个祖先),并对其子节点进行递归过滤。
/**
* 深度复制对象,避免直接修改原数据
* @param {Object} o - 要复制的对象
* @returns {Object} 复制后的对象
*/
function copy(o) {
return Object.assign({}, o);
}
/**
* 递归过滤深度嵌套的对象数组,并保留匹配项的父级层级
* @param {Array<Object>} data - 统一使用 'children' 键的深度嵌套数据数组
* @param {string} searchVal - 搜索值 (不区分大小写)
* @returns {Array<Object>} 过滤后的数据数组,保留了匹配项的父级层级
*/
function filterDeepHierarchy(data, searchVal) {
// 将搜索值转换为小写,方便不区分大小写匹配
const lowerSearchVal = searchVal.toLowerCase();
// 递归过滤函数
function recursiveFilter(node) {
// 检查当前节点是否直接匹配
// 这里可以扩展为检查更多属性,例如 name, description, code 等
const isMatch = (node.name && node.name.toLowerCase().includes(lowerSearchVal)) ||
(node.description && node.description.toLowerCase().includes(lowerSearchVal)) ||
(node.code && node.code.toLowerCase().includes(lowerSearchVal));
// 如果当前节点有子节点
if (node.children && node.children.length > 0) {
// 递归过滤子节点。
// 关键在于:先对子节点进行深拷贝并过滤,然后将过滤后的结果重新赋值给当前节点的 children 属性。
// 如果过滤后的 children 数组长度大于0,说明有子节点匹配,则当前父节点也应该被保留。
const filteredChildren = node.children.map(copy).filter(recursiveFilter);
if (filteredChildren.length > 0) {
// 如果有匹配的子节点,则保留当前节点,并更新其 children
const newNode = copy(node); // 复制当前节点以保持不变性
newNode.children = filteredChildren;
return newNode;
}
}
// 如果当前节点直接匹配,或者其子节点中有匹配项(已在上面处理),则保留
// 否则,不保留
return isMatch ? copy(node) : null; // 如果是叶子节点且匹配,也复制并返回
}
// 对顶层数组进行过滤
return data.map(copy).filter(recursiveFilter);
}3. 示例用法
假设我们已经将原始数据转换成了 transformed_products 结构。
// 假设 transformed_products 已经准备好 // ... (transformed_products 数据结构如上所示) const searchVal = 'PUL'; // 搜索包含 'PUL' 的产品 const filteredResult = filterDeepHierarchy(transformed_products, searchVal); console.log(JSON.stringify(filteredResult, null, 2));
运行上述代码,你将得到一个与期望输出类似的结果,其中所有匹配PUL的产品及其完整的父级层级都被保留。
注意事项与总结
- 数据结构统一化是关键:此解决方案依赖于一个统一的children键来表示所有子节点。如果你的原始数据结构不满足这一点,你需要先进行数据转换。这通常是一个预处理步骤,可以通过一个单独的递归函数实现。
- Immutability (不变性):copy 函数的使用确保了原始数据在过滤过程中不会被修改,这在函数式编程和避免副作用方面是一个好的实践。
- 过滤逻辑可扩展性:recursiveFilter 函数中的 isMatch 逻辑可以轻松扩展,以检查更多属性或实现更复杂的匹配规则(例如正则表达式、多条件组合等)。
- 性能考虑:对于非常庞大的深度嵌套数据集,递归操作可能会有性能开销。在极端情况下,可能需要考虑迭代方法或优化递归深度。然而,对于大多数常见场景,这种递归方法是清晰且高效的。
- 空节点处理:在recursiveFilter中,如果一个节点既不直接匹配,也没有匹配的子节点,它将被过滤掉(返回null,然后filter函数会将其移除)。这正是我们期望的行为。
通过采用这种统一数据结构和自定义递归过滤的方法,我们可以有效地解决在深度嵌套对象数组中保留父级层级的问题,从而生成既精确又具有完整上下文的过滤结果。
以上就是使用递归过滤深度嵌套对象数组并保留父级层级的详细内容,更多请关注其它相关文章!
# 将其
# 泾县网站关键词优化费用
# 德阳整合营销推广费用
# 公司网站建设优化建站建议
# 贵州seo怎么样做
# 青海网站高端建设团队
# 枣阳网站建设团队
# 奉贤区百度网站优化推广
# 金哥短视频seo
# 企业网站推广招商
# 金堂建设网站公司
# 所示
# 有效地
# 我们可以
# js
# 是这样
# 自定义
# 是一个
# 数据结构
# 递归
# red
# 递归函数
# ai
# 正则表达式
# go
# node
# json
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
Win11怎么修改默认浏览器_Windows 11设置Chrome为默认
windows10怎么查看本机ip_windows10命令提示符ipconfig使用
Pandas DataFrame:高效添加条件计算列
邮政快递包裹最新位置 邮政快递实时追踪入口
sublime怎么覆盖插件的默认快捷键_sublime快捷键优先级与设置
实现分段式页面滚动导航:CSS与J*aScript教程
c++ 命名空间怎么用 c++ namespace使用指南
PHP高效扁平化嵌套数组:使用array_merge与数组解包操作符
淘宝网网页版登录入口 淘宝官方网页版快捷登录
J*a里如何使用N*igableMap进行导航操作_可导航Map操作技巧解析
Shopware订单对象中获取产品自定义字段的正确方法
离线运行Go语言之旅:本地部署与GOPATH配置指南
马斯克:Optimus 人形机器人复数形式为 Optimi
一加 Nord 5 隐私权限异常_一加 Nord 5 系统安全优化
fishbowl官网免费版 fishbowl养鱼网站入口
python3时间如何用calendar输出?
优化Django表单:提交验证失败后保留用户输入
LINUX的I/O重定向是什么_深入理解LINUX中 >、>> 与 < 的区别
顺丰快件物流信息 官方网站查询入口
网易大神怎么保存别人动态的图片_网易大神动态图片保存方法
解决 MongoDB 聚合查询中对象数组 _id 匹配问题
Go语言中JSON数据解析与字段访问教程
抖音网页版快捷访问 抖音网页版网页版入口操作教程
照顾宝贝2小游戏免费秒玩入口
高德地图总提示网络异常怎么办 高德地图离线导航设置与网络排查方法
三星ZFold5多任务卡顿_Samsung ZFold5流畅度提升
如何使用纯J*aScript判断Input元素是否在特定类容器内
React中useState与局部变量:理解组件状态管理与渲染机制
AI抖音网页版免费视频入口 AI抖音网页端最新视频实时观看
如何提高微信支付的安全性_微信支付安全防护与设置建议
CSS实现侧边栏导航项全宽圆角悬停背景效果
深入理解Promise链:如何在catch后中断then的执行
抖音商城签到领现金是真的吗_抖音商城签到奖励与提现说明
C++如何打印当前代码行号与文件名_C++预定义宏FILE与LINE的使用
c++ 获取系统当前时间 c++时间戳获取方法
理解Python模块与全局变量的作用域管理
知乎APP怎么管理已购盐选内容_知乎APP盐选内容购买记录与查看方法
小红书商家版怎样在笔记嵌入商品卡路径_小红书商家版在笔记嵌入商品卡路径【挂载教程】
J*aScript数组对象转换:按指定键分组与值收集
俄罗斯浏览器官网直达链接 俄罗斯浏览器最新在线入口导航
Lar*el 8 多关键词数据库搜索优化实践
AO3最新官网入口公告_2025AO3镜像站实时查询方法
Win11截图该按哪些键 Win11截屏完整流程解析【教程】
知音漫客正版漫画平台_知音漫客官网账号登录
HuggingFaceEmbeddings中向量嵌入维度调整的限制与理解
C++如何连接MySQL数据库_C++使用Connector/C++操作MySQL数据库教程
俄罗斯Yandex搜索引擎入口_Yandex官网免登录一键访问
优酷会员付费后没到账怎么办_优酷会员充值异常及解决方法
抖音DOU+怎么投最有效 抖音付费推广的ROI提升技巧
Spring Boot嵌入式服务器与J*a EE:功能支持深度解析


2025-10-16
浏览次数:次
返回列表
products: [
{
type: 'RECTANGULAR',
products_list: [
{ color: 'natural', code: 'PUL46130', description: 'Rectangular tray 950ml' },
{ color: 'clear', code: 'PUL51601', description: 'rPET lid' },
// ...其他匹配产品
]
},
// ...其他匹配类型
]
},
// ...其他匹配子分类
],
},
// ...其他匹配分类
],
},
// ...其他匹配顶级产品组
];