Supongamos que queremos sumar un array de números. Hay muchas formas de lograr este objetivo.
En muchos lenguajes, este problema se expresa como un bucle con una variable acumuladora.
Este problema se puede escribir como una función recursiva. En pseudocódigo, podríamos tener esto.
function Add(X, Sum=0):
if X is empty then
return Sum
else
return Add(rest(X), Sum + first(X))
end
end
Este método de dividir el problema en partes más pequeñas también se puede describir como «reducir hacia el caso base».
Reduce es una forma de combinar todos los elementos de una estructura de datos en un único valor. El proceso itera sobre la estructura de datos, aplicando una función a cada elemento para actualizar el resultado acumulado.
En jq, este proceso se implementa en el filtro reduce.
En otros lenguajes, puede llamarse «fold», «fold-left», «inject» o «aggregate».
La expresión reduce de jq se ve así.
reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
STREAM_EXPRESSION es un flujo de elementos, y cada uno se almacena en la variable $var a su vez.
.[]: $myArray | .[].INITIAL_VALUE es el valor inicial del resultado acumulado (conocido como el «acumulador»).UPDATE_EXPRESSION combina («pliega») el valor actual ($var) en el acumulador.
. es el valor del acumulador.reduce.Veamos un ejemplo: sumar los números de un array.
El filtro add hace exactamente esto, pero veremos cómo implementarlo.
Si usamos [10, 20, 30, 40] como argumento y tomamos cero como estado inicial, así se ve cada paso.
| # | estado | elemento | reductor | resultado |
|---|---|---|---|---|
| 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 la sintaxis de jq, esto se ve así.
0 + 10 | . + 20 | . + 30 | . + 40
Podemos expresar eso con el filtro reduce.
[10, 20, 30, 40] | reduce .[] as $n (0; . + $n) # => 100
La función incorporada add en realidad está implementada con reduce, pero usa «null» como estado inicial (se puede sumar cualquier tipo de dato a null).
def add: reduce .[] as $x (null; . + $x);
En la expresión de reducción, . es el acumulador.
Si el argumento es algún objeto que necesitas referenciar dentro de la función de reducción, tienes que guardarlo en una variable.
{"apple": 10, "banana": 16, "carrot": 4}
| . as $obj
| reduce (keys | .[]) as $key (0; . + $obj[$key]) # => 30
El acumulador puede ser de cualquier tipo de dato. Por ejemplo, quizá quieras invertir un array.
["A", "B", "C", "D"]
| reduce .[] as $elem ([]; [$elem] + .) # => ["D", "C", "B", "A"]
Eres docente. Al final del año, generaste una calificación numérica para cada uno de tus estudiantes. Ahora necesitas convertirla en una calificación con letra y contar cuántos estudiantes obtuvieron cada calificación con letra.
La función letter_grade recibe una calificación numérica como argumento y devuelve la letra.
Usa estos rangos:
| Letra | Calificación |
|---|---|
| A | 90% - 100% |
| B | 80% - 89% |
| C | 70% - 79% |
| D | 60% - 69% |
| F | 0% - 59% |
Ejemplo:
75 | letter_grade # => "C"
La función count_letter_grades recibe un objeto que asigna los nombres de los estudiantes a sus calificaciones.
El resultado será un objeto que asigna cada calificación con letra al número de estudiantes con esa calificación.
Ejemplo:
{"Joe": 78, "Jane": 93, "Richard": 72} | count_letter_grades
# => {"A": 1, "B": 0, "C": 2, "D": 0, "F": 0}
Hay varias maneras distintas de resolver esto.
Usa el filtro reduce para practicar.
Regístrate en Exercism para aprender y dominar jq con 12 conceptos75 ejercicios y mentoría humana real, todo gratis.