Count elements whilst predicate is true

Viewed 127

I have a collection of JSON objects like so:

{"id":"da1234563","message":"brown fox jump over a red log"}
{"id":"da1234564","message":"brown fox jump over a red log"}
{"id":"da1234565","message":"brown fox jump over a red log"}
{"id":"da1234566","message":"brown fox jump over a red log"}
{"id":"da1234567","message":"red fox jump over a brown log"}
{"id":"da1234568","message":"another random string"}
{"id":"da1234569","message":"brown fox jump over a red log"}

I'd like to use jq to iterate over these objects and return the number of brown fox's jumping UNTIL a match is not found.

The following jq gives me every line with brown fox including the last line:

jq -n 'inputs|select(.message|test("brown fox"))'

How do I exclude the matches after a mismatch and count the number of matches so that jq returns only 4?

3 Answers

Another option similar to @customcommander, but using until instead of reduce:

'[0,.] |                                                                                                                                                                                             
 until(
   (.[1] | .[0] | .message | test("brown fox")| not);
   [ .[0]+ 1 , (.[1] | .[1:]) ]) 
| .[0]' 

https://jqplay.org/s/pAyfv7ZvQK

For efficiency, it is best to avoid "slurping" the input, which as envisioned in the Q, can in this case can be done quite simply by using input (or inputs) and the -n command-line option.

Using a stream-oriented counting function and the built-in while:

def count(s): reduce s as $x (0; .+1);

count(while (input | .message|test("brown fox"); true) )

An alternative

def count(s): reduce s as $x (0; .+1);

def select_while(s; cond):
  label $out | s | if cond then . else break $out end;

count( select_while(inputs; .message|test("brown fox") ) )

With --slurp:

reduce .[] as $i ([0, true]; if .[1] and ($i.message | contains("brown fox")) then .[0] += 1 else .[1] = false end) | .[0]

I use [0, true] to know when to stop counting:

  1. The boolean indicate that we should keep counting
  2. The number is incremented whilst we keep matching "brown fox"
  3. However as soon as we don't the flag is changed to false

I'm sure there's a better way but I just can't see it ;)


https://jqplay.org/s/Art_0y3u_g

Related