2.8 KiB
Reduce is a powerful way to process a collection of data into a new form.
<exp> as $<name> ireduce (<init>; <block>)
e.g.
.[] as $item ireduce (0; . + $item)
On the LHS we are configuring the collection of items that will be reduced <exp>
as well as what each element will be called $<name>
. Note that the array has been splatted into its individual elements.
On the RHS there is <init>
, the starting value of the accumulator and <block>
, the expression that will update the accumulator for each element in the collection.
Note that within the block expression, .
will evaluate to the current value of the accumulator. This effectively means that within the reduce
block you can no longer access data other than elements of the array set as $<name>
. For simple things, this is probably fine, but often you will need to refer to other data elements.
This can be done by setting a variable using as
and piping that into the reduce
operation, or you can simply refer to $context
which is exactly that, automatically set for you for convenience. See examples below.
yq vs jq syntax
Reduce syntax in yq
is a little different from jq
- as yq
(currently) isn't as sophisticated as jq
and its only supports infix notation (e.g. a + b, where the operator is in the middle of the two parameters) - where as jq
uses a mix of infix notation with prefix notation (e.g. reduce a b
is like writing + a b
).
To that end, the reduce operator is called ireduce
for backwards compatability if a prefix version of reduce
is ever added.
Sum numbers
Given a sample.yml file of:
- 10
- 2
- 5
- 3
then
yq eval '.[] as $item ireduce (0; . + $item)' sample.yml
will output
20
Convert an array to an object
Given a sample.yml file of:
- name: Cathy
has: apples
- name: Bob
has: bananas
then
yq eval '.[] as $item ireduce ({}; .[$item | .name] = ($item | .has) )' sample.yml
will output
Cathy: apples
Bob: bananas
Merge all documents together - using context
The $context variable set by reduce lets you access the data outside the reduce block.
Given a sample.yml file of:
a: cat
And another sample another.yml file of:
b: dog
then
yq eval-all 'fileIndex as $item ireduce ({}; . * ($context | select(fileIndex==$item)) )' sample.yml another.yml
will output
a: cat
b: dog
Merge all documents together - without using context
$context
is just a convenient variable that reduce
sets, you can use your own for more control
Given a sample.yml file of:
c:
a: cat
And another sample another.yml file of:
c:
b: dog
then
yq eval-all '.c as $root | fileIndex as $item ireduce ({}; . * ($root | select(fileIndex==$item)) )' sample.yml another.yml
will output
a: cat
b: dog