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 ed una variabile accumulatore.
Questo problema si può scrivere come funzione ricorsiva. In pseudo-codice, 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 pezzi più piccoli si può descrivere anche come «ridurre verso il caso base».
La riduzione è un modo per combinare tutti gli elementi di una struttura dati in un unico valore. Il processo esegue un'iterazione sulla struttura dati, applicando una funzione ad ogni elemento per aggiornare il risultato accumulato.
In jq, questo processo è implementato nel filtro reduce.
In altri linguaggi, può essere chiamato "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, ciascuno 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 proprio questo, ma vedremo come implementarlo.
Se usiamo [10, 20, 30, 40] come input e prendiamo zero come stato iniziale, ecco come si presenta ogni passo.
| # | 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, si presenta come questo codice.
0 + 10 | . + 20 | . + 30 | . + 40
Possiamo esprimere la stessa cosa 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ò sommare qualsiasi tipo di dato).
def add: reduce .[] as $x (null; . + $x);
Nell'espressione di riduzione, . è l'accumulatore.
Se l'input è un oggetto che devi referenziare 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"]
Sei un insegnante. Alla fine dell'anno hai assegnato un voto numerico a ciascuno dei tuoi studenti. Ora devi convertire quel voto in un voto in lettere e contare quanti studenti hanno ottenuto ciascun voto.
La funzione letter_grade prende in input un voto numerico e produce in output la lettera.
Usa questi intervalli:
| Lettera | Voto |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
Esempio:
75 | letter_grade # => "C"
La funzione count_letter_grades prende un oggetto che mappa i nomi degli studenti ai loro voti.
L'output sarà un oggetto che mappa ogni voto in lettere al numero di studenti con quel voto.
Esempio:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Ci sono diversi modi per risolvere questo problema.
Usa il filtro reduce per esercitarti.
Iscriviti a Exercism per imparare e padroneggiare jq con 12 concetti75 esercizi e il mentoring di persone reali, tutto gratis.