Supposons que l'on veuille additionner un tableau de nombres. Il existe de nombreuses façons d'atteindre cet objectif.
Dans de nombreux langages, ce problème s'exprime par une boucle avec une variable d'accumulation.
Ce problème peut s'écrire sous la forme d'une fonction récursive. En pseudo-code, on pourrait avoir ceci.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Cette méthode de découpage du problème en morceaux plus petits peut aussi se décrire comme « réduire vers le cas de base ».
Reduce est une façon de combiner tous les éléments d'une structure de données en une seule valeur. Le processus parcourt la structure de données en appliquant une fonction à chaque élément afin de mettre à jour le résultat accumulé.
En jq, ce processus est implémenté par le filtre reduce.
Dans d'autres langages, on l'appelle parfois « fold », « fold-left », « inject » ou « aggregate ».
L'expression reduce de jq ressemble à ceci.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION est un flux d'éléments, chacun stocké tour à tour dans la variable $var.
.[] : $myArray | .[].INITIAL_VALUE est la valeur de départ du résultat accumulé (que l'on appelle l'« accumulateur »).UPDATE_EXPRESSION combine (« replie ») la valeur courante ($var) dans l'accumulateur.
. est la valeur de l'accumulateur.reduce.Prenons un exemple : additionner les nombres d'un tableau.
Le filtre add fait exactement cela, mais on va voir comment l'implémenter.
Si on utilise [10, 20, 30, 40] comme entrée et zéro comme état initial, voici à quoi ressemble chaque étape.
| n° | état | élément | réduction | résultat |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
En syntaxe jq, cela ressemble à ce code.
0 + 10 | . + 20 | . + 30 | . + 40
On peut exprimer cela avec le filtre reduce.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
La fonction intégrée add est en réalité implémentée avec reduce, mais elle utilise « null » comme état initial (tout type de données peut être ajouté à null).
def add: reduce .[] as $x (null; . + $x);
Dans l'expression de réduction, . est l'accumulateur.
Si l'entrée est un objet auquel tu dois faire référence à l'intérieur de la fonction de réduction, tu dois le stocker dans une variable.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
L'accumulateur peut être de n'importe quel type de données. Par exemple, tu peux vouloir inverser un tableau.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]