Tracks
/
jq
jq
/
Temario
/
Reduce
Re

Reduce en jq

2 ejercicios

Acerca de Reduce

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 algo así.

    function Add(X, Sum=0):
      if X is empty then
        return Sum
      else
        return Add(rest(X), Sum + first(X))
      end
    end
    

    Esta forma de dividir el problema en partes más pequeñas también puede describirse 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 recorre la estructura de datos y aplica 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 tiene este aspecto.

reduce STREAM_EXPRESSION as $var (INITIAL_VALUE; UPDATE_EXPRESSION)
  • STREAM_EXPRESSION es un stream de elementos, cada uno guardado por turno en la variable $var.
    • Recuerda que, para recorrer un array como un stream, usa el filtro iterador .[]: $myArray | .[].
  • INITIAL_VALUE es el valor inicial del resultado acumulado (conocido como el «acumulador»).
  • La UPDATE_EXPRESSION combina («pliega») el valor actual ($var) en el acumulador.
    • En el contexto de esta expresión, . es el valor del acumulador.
    • La salida de la expresión se guarda en el acumulador para usarla en la siguiente iteración.
    • Tras la última iteración, el resultado acumulado es la salida de 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 entrada y tomamos cero como estado inicial, cada paso tiene este aspecto.

# 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, eso se ve así.

0 + 10 | . + 20 | . + 30 | . + 40

Podemos expresarlo con el filtro reduce.

[10, 20, 30, 40] | reduce .[] as $n (0; . + $n)     # => 100
Note

La función integrada add en realidad está implementada con reduce, pero usa «null» como estado inicial (a null se le puede añadir cualquier tipo de datos).

def add: reduce .[] as $x (null; . + $x);

Algunas cosas que tener en cuenta

  • En la expresión de reducción, . es el acumulador. Si la entrada es un objeto al que necesitas hacer referencia dentro de la función reductora, 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 datos. Por ejemplo, puede que quieras invertir un array.

    ["A", "B", "C", "D"]
    | reduce .[] as $elem ([]; [$elem] + .)       # => ["D", "C", "B", "A"]
    
Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Reduce

La práctica está bloqueada

Desbloquea 3 ejercicios más para practicar Reduce