Supposons que l'on veuille additionner un tableau de nombres. Il existe de nombreuses façons d'atteindre ce but.
Dans de nombreux langages, ce problème s'exprime sous la forme d'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, qui consiste à diviser le problème en plus petits morceaux, peut aussi se décrire comme le fait de « 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 pour mettre à jour le résultat accumulé.
Dans 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 stream d'éléments, chacun stocké tour à tour dans la variable $var.
.[] : $myArray | .[].INITIAL_VALUE est la valeur de départ du résultat accumulé (appelé « accumulateur »).UPDATE_EXPRESSION combine (« fold ») 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.
| # | état | élément | réducteur | 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 native add est en réalité implémentée avec reduce, mais elle utilise « null » comme état initial (n'importe quel 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"]
Tu es professeur. À la fin de l'année, tu as attribué une note chiffrée à chacun de tes élèves. Tu dois maintenant la traduire en lettre et compter combien d'élèves ont obtenu chaque lettre
La fonction letter_grade prend une note chiffrée en entrée et renvoie la lettre correspondante.
Utilise les intervalles suivants :
| Lettre | Note |
|---|---|
| A | 90 % - 100 % |
| B | 80 % - 89 % |
| C | 70 % - 79 % |
| D | 60 % - 69 % |
| F | 0 % - 59 % |
Exemple :
75 | letter_grade # => "C"
La fonction count_letter_grades prend un objet qui associe le nom des élèves à leurs notes.
Le résultat est un objet qui associe chaque lettre au nombre d'élèves ayant obtenu cette note.
Exemple :
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Il existe plusieurs façons de résoudre ce problème.
Utilise le filtre reduce pour t'entraîner.
Inscris-toi sur Exercism pour apprendre et maîtriser jq avec 12 concepts75 exercices, et un vrai mentorat humain, le tout gratuitement.