通过在jq中一对一比较两个流来过滤
Filtering by comparing two streams one-on-one in jq
我有流
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "c",
"value": 1
}
{
"key": "d",
"value": 1
}
{
"key": "e",
"value": 1
}
和
(true,true,false,false,true)
我想比较两个 一对一 并且只在对应的布尔值是 true
.
时才打印对象
所以我要输出
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "e",
"value": 1
}
我试过了(https://jqplay.org/s/GGTHEfQ9s3)
filter:
. as $input | foreach (true,true,false,false,true) as $dict ($input; select($dict))
input:
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "c",
"value": 1
}
{
"key": "d",
"value": 1
}
{
"key": "e",
"value": 1
}
但我得到输出:
{"key":"a","value":1}
{"key":"a","value":1}
null
{"key":"b","value":1}
{"key":"b","value":1}
null
{"key":"c","value":1}
{"key":"c","value":1}
null
{"key":"d","value":1}
{"key":"d","value":1}
null
{"key":"e","value":1}
{"key":"e","value":1}
null
我们将不胜感激。
一种方法是将流作为数组读取,使用 transpose
来匹配它们的项目,然后 select
一个并输出另一个:
jq -s '[.,[(true,true,false,false,true)]] | transpose[] | select(.[1])[0]' objects.json
另一种方法是将流作为数组读取,将布尔数组转换为条件匹配的 indices
,并使用它们引用对象数组:
jq -s '.[[(true,true,false,false,true)] | indices(true)[]]' objects.json
相同的方法,但使用 nth
引用 inputs
流需要更多的预防措施,因为流输入的连续消耗需要提供相对距离,而不是 [=18 的绝对位置=].可以通过使用 index
和 while
循环连续检查下一个 true
值的位置来实现转换:
jq -n 'nth([true,true,false,false,true] | while(. != []; .[index(true) + 1:]) | index(true) | values; inputs)' objects.json
也可以使用 reduce
直接遍历布尔值,只需 select
任何合适的 input
:
jq -n 'reduce (true,true,false,false,true) as $dict ([]; . + [input | select($dict)]) | .[]' objects.json
使用 foreach
的解决方案,如您所愿,还需要 -n
选项才能不错过第一项:
jq -n 'foreach (true,true,false,false,true) as $dict (null; input | select($dict))' objects.json
不幸的是,目前每次调用 jq 最多只能处理一个外部 JSON 流。除非两个流都非常大,否则这通常不是问题,因此在这个答案中我将重点关注可扩展的解决方案。事实上,无论流有多大,所需的计算机内存量都是微不足道的。
为简单起见,我们假设:
- demon.json 是一个由 JSON 布尔值流组成的文件(即不是 comma-separated);
- object.json 是您的 JSON 对象流;
- 流的长度相同;
- 我们在 bash 或 bash-like 环境中工作。
然后我们可以选择:
paste -d '\t' demon.json <(jq -c . objects.json) | jq -n '
foreach inputs as $boolean (null; input; select($boolean))'
所以除了paste
和jq
的启动成本外,我们基本上一次只需要足够的内存来容纳objects.json中的一个对象。这个解决方案也很快。
当然,如果 objects.json 已经是 JSONL (JSON-lines) 格式,那么上面第一次调用 jq 就没有必要了。
我有流
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "c",
"value": 1
}
{
"key": "d",
"value": 1
}
{
"key": "e",
"value": 1
}
和
(true,true,false,false,true)
我想比较两个 一对一 并且只在对应的布尔值是 true
.
所以我要输出
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "e",
"value": 1
}
我试过了(https://jqplay.org/s/GGTHEfQ9s3)
filter:
. as $input | foreach (true,true,false,false,true) as $dict ($input; select($dict))
input:
{
"key": "a",
"value": 1
}
{
"key": "b",
"value": 1
}
{
"key": "c",
"value": 1
}
{
"key": "d",
"value": 1
}
{
"key": "e",
"value": 1
}
但我得到输出:
{"key":"a","value":1}
{"key":"a","value":1}
null
{"key":"b","value":1}
{"key":"b","value":1}
null
{"key":"c","value":1}
{"key":"c","value":1}
null
{"key":"d","value":1}
{"key":"d","value":1}
null
{"key":"e","value":1}
{"key":"e","value":1}
null
我们将不胜感激。
一种方法是将流作为数组读取,使用 transpose
来匹配它们的项目,然后 select
一个并输出另一个:
jq -s '[.,[(true,true,false,false,true)]] | transpose[] | select(.[1])[0]' objects.json
另一种方法是将流作为数组读取,将布尔数组转换为条件匹配的 indices
,并使用它们引用对象数组:
jq -s '.[[(true,true,false,false,true)] | indices(true)[]]' objects.json
相同的方法,但使用 nth
引用 inputs
流需要更多的预防措施,因为流输入的连续消耗需要提供相对距离,而不是 [=18 的绝对位置=].可以通过使用 index
和 while
循环连续检查下一个 true
值的位置来实现转换:
jq -n 'nth([true,true,false,false,true] | while(. != []; .[index(true) + 1:]) | index(true) | values; inputs)' objects.json
也可以使用 reduce
直接遍历布尔值,只需 select
任何合适的 input
:
jq -n 'reduce (true,true,false,false,true) as $dict ([]; . + [input | select($dict)]) | .[]' objects.json
使用 foreach
的解决方案,如您所愿,还需要 -n
选项才能不错过第一项:
jq -n 'foreach (true,true,false,false,true) as $dict (null; input | select($dict))' objects.json
不幸的是,目前每次调用 jq 最多只能处理一个外部 JSON 流。除非两个流都非常大,否则这通常不是问题,因此在这个答案中我将重点关注可扩展的解决方案。事实上,无论流有多大,所需的计算机内存量都是微不足道的。
为简单起见,我们假设:
- demon.json 是一个由 JSON 布尔值流组成的文件(即不是 comma-separated);
- object.json 是您的 JSON 对象流;
- 流的长度相同;
- 我们在 bash 或 bash-like 环境中工作。
然后我们可以选择:
paste -d '\t' demon.json <(jq -c . objects.json) | jq -n '
foreach inputs as $boolean (null; input; select($boolean))'
所以除了paste
和jq
的启动成本外,我们基本上一次只需要足够的内存来容纳objects.json中的一个对象。这个解决方案也很快。
当然,如果 objects.json 已经是 JSONL (JSON-lines) 格式,那么上面第一次调用 jq 就没有必要了。