如何用Bash和jq过滤对象数组并保留匹配项前后X个对象
解决方法:用jq高效筛选匹配项及前后指定数量的元素
要实现筛选所有type.name='Pass'的对象,同时保留每个匹配对象前后2个范围内的元素,并且高效处理14万+行的大文件,我们可以通过索引标记+批量过滤的思路来实现,避免重复遍历数组,保证效率。
核心思路
- 先定位所有
type.name='Pass'元素的索引位置; - 基于这些索引,计算出需要保留的索引范围(每个匹配索引的前后2个,同时处理数组边界,避免越界);
- 把需要保留的索引做成快速查找的集合,最后过滤原数组中对应索引的元素。
jq实现命令
jq ' # 第一步:收集所有type.name为Pass的元素索引 ( [ to_entries[] | select(.value.type.name == "Pass") | .key ] as $pass_indices # 第二步:生成需要保留的索引集合(每个Pass索引前后2个,含自身) | reduce $pass_indices[] as $i ({}; reduce ($i-2) to ($i+2) as $j (.; # 确保索引在数组有效范围内(不小于0,不超过数组长度-1) if $j >= 0 and $j < length then .[$j|tostring] = true else . end ) ) as $keep_indices # 第三步:过滤原数组,保留索引在集合中的元素 | [ to_entries[] | select($keep_indices[.key|tostring]) | .value ] ' file.json
如果需要输出紧凑格式(适合大文件传输或存储),添加-c参数:
jq -c '...' file.json
命令详解
to_entries:将原数组转换为{"key": 索引, "value": 原元素}的对象数组,方便我们获取每个元素的位置;- 收集Pass索引:遍历转换后的数组,筛选出
value.type.name == "Pass"的项,提取它们的key(即原数组中的索引),存入$pass_indices变量; - 生成保留索引集合:通过两次
reduce,遍历每个Pass索引,将其前后2个索引(含自身)标记为需要保留的索引,同时处理边界情况(比如第一个元素的前2个索引会小于0,直接忽略); - 过滤元素:再次遍历转换后的数组,只保留索引在
$keep_indices集合中的元素,最后转回原数组格式。
调整保留范围
如果需要修改前后保留的元素数量(比如改为前后1个),只需要把命令中的$i-2和$i+2替换为$i-1和$i+1即可,非常灵活。
效率说明
这个方案的时间复杂度是O(n)(n为数组长度),仅需三次线性遍历(转换数组、收集索引、过滤元素),且索引查找是O(1)的哈希表查询,完全可以高效处理14万+行的大JSON文件,不会出现性能瓶颈。
内容的提问来源于stack exchange,提问作者Ed Rogers
相关产品推荐
相关产品推荐

