Search code examples
streamcomparejq

Filtering by comparing two streams one-on-one in jq


I have streams

{
    "key": "a",
    "value": 1
}
{
    "key": "b",
    "value": 1
}
{
    "key": "c",
    "value": 1
}
{
    "key": "d",
    "value": 1
}
{
    "key": "e",
    "value": 1
}

And

 (true,true,false,false,true)

I want to compare the two one-on-one and only print the object if the corresponding boolean is true.

So I want to output

{
    "key": "a",
    "value": 1
}
{
    "key": "b",
    "value": 1
}
{
    "key": "e",
    "value": 1
}

I tried (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
}

But I get output:

{"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

Help will be appreciated.


Solution

  • One way would be to read in the streams as arrays, use transpose to match their items, and select by one and output the other:

    jq -s '[.,[(true,true,false,false,true)]] | transpose[] | select(.[1])[0]' objects.json
    

    Demo

    Another approach would be to read in the streams as arrays, convert the booleans array into those indices where conditions match, and use them to reference into the objects array:

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

    Demo

    The same approach but using nth to reference into the inputs stream requires more precaution, as the successive consumption of stream inputs demands the provision of relative distances, not absolute positions to nth. A conversion can be implemented by successively checking the position of the next true value using index and a while loop:

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

    Demo

    One could also use reduce to directly iterate over the boolean values, and just select any appropriate input:

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

    Demo

    A solution using foreach, like you intended, also would need the -n option to not miss the first item:

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

    Demo