Supponiamo di voler sommare un array di numeri. Ci sono molti modi per raggiungere questo obiettivo.
In molti linguaggi, questo problema si esprime con un ciclo e una variabile accumulatore.
Questo problema può essere scritto come una funzione ricorsiva. In pseudocodice, potremmo avere questo.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Questo metodo di suddividere il problema in parti più piccole si può descrivere anche come «ridurre verso il caso base».
Reduce è un modo per combinare tutti gli elementi di una struttura dati in un unico valore. Il processo itera sulla struttura dati, applicando una funzione a ogni elemento per aggiornare il risultato accumulato.
In jq, questo processo è implementato nel filtro reduce.
In altri linguaggi potrebbe chiamarsi «fold», «fold-left», «inject» o «aggregate».
L'espressione reduce di jq si presenta così.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION è uno stream di elementi, ognuno memorizzato a turno nella variabile $var.
.[]: $myArray | .[].INITIAL_VALUE è il valore iniziale del risultato accumulato (noto come «accumulatore»).UPDATE_EXPRESSION combina («fold») il valore corrente ($var) nell'accumulatore.
. è il valore dell'accumulatore.reduce.Vediamo un esempio: sommare i numeri di un array.
Il filtro add fa esattamente questo, ma vedremo come implementarlo.
Se usiamo [10, 20, 30, 40] come input e prendiamo zero come stato iniziale, ogni passo si presenta così.
| # | stato | elemento | riduttore | risultato |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
Nella sintassi di jq, il codice è questo.
0 + 10 | . + 20 | . + 30 | . + 40
Possiamo esprimerlo con il filtro reduce.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
La funzione incorporata add in realtà è implementata con reduce, ma usa «null» come stato iniziale (a null si può aggiungere qualsiasi tipo di dato).
def add: reduce .[] as $x (null; . + $x);
Nell'espressione di riduzione, . è l'accumulatore.
Se l'input è un oggetto a cui devi fare riferimento all'interno della funzione di riduzione, devi memorizzarlo in una variabile.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
L'accumulatore può essere di qualsiasi tipo di dato. Per esempio potresti voler invertire un array.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]