Matt
Matt

Reputation: 10833

Cumulative sum in jq

I have a series of [timestamp, count] pairs in an array and a want to compute the cumulative sum at each timestamp using jq. How could I do that?

Here a sample data set:

[
  [1431047957699, 1],
  [1431047958269, 1],
  [1431047958901, 1],
  [1431047959147, -1],
  [1431047960164, 1]
]

And the expected result:

[1431047957699, 1],
[1431047958269, 2],
[1431047958901, 3],
[1431047959147, 2],
[1431047960164, 3]

Is it possible to do this with jq?

Upvotes: 2

Views: 2067

Answers (2)

peak
peak

Reputation: 116900

The following is quite general (e.g. it can be used with an array of objects):

def accumulate(f):
  reduce .[1:][] as $row
    ([.[0]];
     . as $x
     | $x + [ $row | (f = ($x | .[length-1] | f) + ($row|f)  ) ] );

accumulate(.[1])

If you are using a sufficiently recent version of jq, then "$x | .[length-1]" can be simplified to "$x[-1]".

Solution using foreach

If your jq has foreach, then the following variant can be used. It would be particularly appropriate if a stream of values rather than array is wanted.

def accumulates(f):
  foreach .[] as $row
    (0;
     . + ($row | f) ;
     . as $x | $row | (f = $x));

Usage:

For a stream: accumulates(.[0])

For an array: [accumulates(.[0])

Upvotes: 4

Jeff Mercado
Jeff Mercado

Reputation: 134521

Take a functional approach to this and create an update function that will create the updated values with the cumulative sum.

def accumulate(acc):
    select(length > 0) |
    (.[0][1] + acc) as $next |
    (.[0] | .[1] = $next), (.[1:] | accumulate($next))
    ;
[accumulate(0)]

Here, we break the array into "head" and "tail" updating the head with the current sum and recursively update the tail. The results are placed back into a new array.

Upvotes: 2

Related Questions