how to accomplish each_slice like ruby with jq

Viewed 72

Sample Input

[1,2,3,4,5,6,7,8,9]

My Solution

$ echo '[1,2,3,4,5,6,7,8,9]' | jq --arg g 4 '. as $l|($g|tonumber) as $n |$l|length as $c|[range(0;$c;($g|tonumber))]|map($l[.:.+$n])' -c

Output

[[1,2,3,4],[5,6,7,8],[9]]

shorthand, handy method anything else?

3 Answers

Use a while loop to chop off the first 4 elements .[4:] until the array is empty []. Then, for each result array, consider only its first 4 items [:4]. Generalized to $n:

jq -c --argjson n 4 '[while(. != []; .[$n:])[:$n]]'
[[1,2,3,4],[5,6,7,8],[9]]

Demo

One way using reduce operating on the whole list, forming only n entries (sub-arrays) at a time

jq -c --argjson g 4 '. as $input | 
  reduce range(0; ( $input | length ) ; $g) as $r ( []; . + [ $input[ $r: ( $r + $g ) ] ] )'

The three argument form of range(from: upto; by) generates numbers from to upto with an increment of by

E.g. range(0; 9; 4) from your original input produces a set of indices - 0, 4, 8 which is ranged over and the final list is formed by appending the slices, coming out of the array slice operation e.g. [0:4], [4:8] and [8:12]

There's an undocumented builtin function, _nwise/1, which you would use like this:

jq -nc --argjson n 4 '[1,2,3,4,5,6,7,8,9] | [_nwise($n)]'

[[1,2,3,4],[5,6,7,8],[9]]

Notice that using --argjson allows you to avoid the call to tonumber.

Related