jq · lesson 19 of 32 · Aggregation
reduce & foreach
reduce stream as $x (start; update) folds a stream into a single value. foreach is the same, but emits the state after every step.
Examples
Example 1Count members per role
Document
{
"team": "Platform",
"office": { "city": "Berlin", "floor": 3 },
"members": [
{ "name": "Ada", "age": 36, "role": "lead", "active": true,
"email": "[email protected]", "skills": ["rust", "sql"] },
{ "name": "Linus", "age": 28, "role": "dev", "active": true,
"email": null, "skills": ["c", "shell"] },
{ "name": "Grace", "age": 45, "role": "dev", "active": false,
"skills": ["cobol"] },
{ "name": "Alan", "age": 41, "role": "qa", "active": true,
"email": "[email protected]", "skills": [] }
]
}Query
reduce .members[] as $m ({}; .[$m.role] += 1)
reduce .members[] as $mLoop over the stream, binding each element to$m.{}The starting value: an empty object..[$m.role] += 1Each round: bump the counter stored under this member's role (a missing key counts as 0).
Result
{"lead":1,"dev":2,"qa":1}Example 2A running total of ages
Document
{
"team": "Platform",
"office": { "city": "Berlin", "floor": 3 },
"members": [
{ "name": "Ada", "age": 36, "role": "lead", "active": true,
"email": "[email protected]", "skills": ["rust", "sql"] },
{ "name": "Linus", "age": 28, "role": "dev", "active": true,
"email": null, "skills": ["c", "shell"] },
{ "name": "Grace", "age": 45, "role": "dev", "active": false,
"skills": ["cobol"] },
{ "name": "Alan", "age": 41, "role": "qa", "active": true,
"email": "[email protected]", "skills": [] }
]
}Query
[foreach (.members[] | .age) as $a (0; . + $a)]
foreachLikereduce, but outputs the state after every step instead of only the last.(.members[] | .age) as $aThe stream to loop over, and the variable holding each item.0The starting state.; . + $aThe update: new state = old state (.) plus this age.
Result
[36,64,109,150]
Good to know
- Inside the update expression,
.is the accumulated state — not the input you started with.