通过在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

Demo

另一种方法是将流作为数组读取,将布尔数组转换为条件匹配的 indices,并使用它们引用对象数组:

jq -s '.[[(true,true,false,false,true)] | indices(true)[]]' objects.json

Demo

相同的方法,但使用 nth 引用 inputs 流需要更多的预防措施,因为流输入的连续消耗需要提供相对距离,而不是 [=18 的绝对位置=].可以通过使用 indexwhile 循环连续检查下一个 true 值的位置来实现转换:

jq -n 'nth([true,true,false,false,true] | while(. != []; .[index(true) + 1:]) | index(true) | values; inputs)' objects.json

Demo

也可以使用 reduce 直接遍历布尔值,只需 select 任何合适的 input:

jq -n 'reduce (true,true,false,false,true) as $dict ([]; . + [input | select($dict)]) | .[]' objects.json

Demo

使用 foreach 的解决方案,如您所愿,还需要 -n 选项才能不错过第一项:

jq -n 'foreach (true,true,false,false,true) as $dict (null; input | select($dict))' objects.json

Demo

不幸的是,目前每次调用 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))'

所以除了pastejq的启动成本外,我们基本上一次只需要足够的内存来容纳objects.json中的一个对象。这个解决方案也很快。

当然,如果 objects.json 已经是 JSONL (JSON-lines) 格式,那么上面第一次调用 jq 就没有必要了。