Angenommen, wir möchten ein Array von Zahlen summieren. Es gibt viele Möglichkeiten, dieses Ziel zu erreichen.
In vielen Sprachen drückt man dieses Problem als Schleife mit einer Akkumulatorvariable aus.
Man kann dieses Problem als rekursive Funktion schreiben. In Pseudocode könnte das so aussehen.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Diese Methode, das Problem in kleinere Teile zu zerlegen, kann man auch als „Reduzieren auf den Basisfall“ beschreiben.
Reduzieren ist eine Möglichkeit, alle Elemente einer Datenstruktur zu einem einzigen Wert zusammenzufassen. Der Prozess iteriert über die Datenstruktur und wendet auf jedes Element eine Funktion an, um das akkumulierte Ergebnis zu aktualisieren.
In jq ist dieser Prozess im reduce-Filter implementiert.
In anderen Sprachen wird es vielleicht „fold“, „fold-left“, „inject“ oder „aggregate“ genannt.
Der reduce-Ausdruck in jq sieht so aus.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION ist ein Stream von Elementen, die nacheinander in der Variable $var gespeichert werden.
.[]: $myArray | .[].INITIAL_VALUE ist der Startwert des akkumulierten Ergebnisses (bekannt als der „Akkumulator“).UPDATE_EXPRESSION kombiniert („faltet“) den aktuellen Wert ($var) in den Akkumulator.
. der Wert des Akkumulators.reduce.Schauen wir uns ein Beispiel an: die Zahlen in einem Array addieren.
Der Filter add macht genau das, aber wir werden sehen, wie man ihn implementiert.
Wenn wir [10, 20, 30, 40] als Eingabe verwenden und null als Anfangszustand nehmen, sieht jeder Schritt so aus.
| # | Zustand | Element | Reducer | Ergebnis |
|---|---|---|---|---|
| 1 | 0 | 10 | 0 + 10 | 10 |
| 2 | 10 | 20 | 10 + 20 | 30 |
| 3 | 30 | 30 | 30 + 30 | 60 |
| 4 | 60 | 40 | 60 + 40 | 100 |
In der jq-Syntax sieht das wie dieser Code aus.
0 + 10 | . + 20 | . + 30 | . + 40
Wir können das mit dem reduce-Filter ausdrücken.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
Das eingebaute add ist tatsächlich mit reduce implementiert, verwendet aber „null“ als Anfangszustand (jeder Datentyp kann zu null addiert werden).
def add: reduce .[] as $x (null; . + $x);
Im reduzierenden Ausdruck ist . der Akkumulator.
Wenn die Eingabe ein Objekt ist, auf das du innerhalb der reduzierenden Funktion verweisen musst, musst du es in einer Variable speichern.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
Der Akkumulator kann von jedem Datentyp sein. Zum Beispiel möchtest du vielleicht ein Array umkehren.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]
Du bist Lehrer. Am Ende des Jahres hast du für jeden deiner Schüler eine Note als Zahl erzeugt. Jetzt musst du diese in eine Buchstabennote umwandeln und zählen, wie viele Schüler jede Buchstabennote erreicht haben
Die Funktion letter_grade nimmt eine Note als Zahl entgegen und gibt den Buchstaben aus.
Verwende diese Bereiche:
| Buchstabe | Note |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
Beispiel:
75 | letter_grade # => "C"
Die Funktion count_letter_grades nimmt ein Objekt entgegen, das Schülernamen ihren Noten zuordnet.
Die Ausgabe ist ein Objekt, das jeder Buchstabennote die Anzahl der Schüler mit dieser Note zuordnet.
Beispiel:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Es gibt mehrere Möglichkeiten, das zu lösen.
Verwende zum Üben den reduce-Filter.
Melde dich bei Exercism an, um jq mit 12 Konzepte75 Übungen und echtem menschlichen Mentoring zu lernen und zu meistern, alles kostenlos.