JavaScript: reduce a profundidad con ejemplos
Tags: javascript, webdev, programming, spanish
Published: 2023-01-08 00:28:04

`reduce()` es uno de los *array methods* mas confusos de comprender en javascript, hasta la fecha me cuesta comprenderlos por poco, sobre todo para casos de uso avanzados. Por ello nace este post donde con ejercicios tratare de entender y compartir todo sobre `reduce`

## **Ejemplo #1: Sumar elementos de un arreglo**
Quiza el ejemplo mas basico y mas ejemplificado de `reduce` veamos:

{% stackblitz js-ftfj9k %}

Veamos lo que hace `reduce` paso a paso:

* Indicamos que el valor inicial de `acc` es `0`, entonces `current` toma el primer elemento del arreglo `numeros`, osea `5`; 
y los suma.

| Iteración | acc  | current |
| --------- | ---- | ------- |
| 1ra       | 0    | 5       |
| 2da       | 5    | 12      |
| 3ra       | 17   | 9       |
| 4ta       | 26   | 2       |
| 5ta       | 28   | 4       |
| 6ta       | 32   | -       |


* En la 2da iteración `acc` tiene ahora el valor de `5` y current es `12`, la suma seria `17`, que es el nuevo valor de `acc` para la 3ra iteración.

* Continuamos haciendo este proceso hasta que el arreglo `numeros` no tenga mas elmentos. Resultado final: `32`.

## **Ejemplo #2: Concatenar todos los elementos de un arreglo numérico**

Dado un arreglo de numeros, concatenar todos sus elementos
{% stackblitz js-qmb8qf %}

| Iteración | acc     | current | Resultado parcial |
| --------- | ------- | ------- | ----------------- |
| 1ra       | ''      | 1       | '1'               |
| 2da       | '1'     | 2       | '12'              |
| 3ra       | '12'    | 3       | '123'             |
| 4ta       | '123'   | 4       | '1234'            |
| 5ta       | '1234'  | 5       | '12345'           |
| 6ta       | '12345' | -       | -                 |

* En la 1ra iteracion `acc` es una cadena vacia que se concatena con el numero `1`, ergo existe una conversion implicita de tipos, de `number` a `string`.

* `acc` ahora es `'1'` como `string` que se concatena con el siguiente valor, un `2`, dando como resultado `12`.

* Se repite el proceso hasta que no queden valores en el arreglo.

> *Note la importancia del valor de inicializacion de `acc`*

## **Ejemplo #3: Sumatoria de coincidencias en arreglo de objetos**

Dado el siguiente arreglo de objetos:

```js
const voters = [
    {name:'Bob' , age: 30, voted: true},
    {name:'Jake' , age: 32, voted: true},
    {name:'Kate' , age: 25, voted: false},
    {name:'Sam' , age: 20, voted: false},
    {name:'Phil' , age: 21, voted: true},
    {name:'Ed' , age:55, voted:true},
    {name:'Tami' , age: 54, voted:true},
    {name: 'Mary', age: 31, voted: false},
    {name: 'Becky', age: 43, voted: false},
    {name: 'Joey', age: 41, voted: true},
    {name: 'Jeff', age: 30, voted: true},
    {name: 'Zack', age: 19, voted: false}
];
```
Contar cuantas personas votaron.

{% stackblitz js-sm9ive %}

| Iteración | acc  | current                                | current.voted | acc + current.voted |
| --------- | ---- | -------------------------------------- | ------------- | ------------------- |
| 1ra       | 0    | {name:'Bob' , age: 30, voted: true}    | true          | 1                   |
| 2da       | 1    | {name:'Jake' , age: 32, voted: true}   | true          | 2                   |
| 3ra       | 2    | {name:'Kate' , age: 25, voted: false}  | false         | 2                   |
| 4ta       | 2    | {name:'Sam' , age: 20, voted: false}   | false         | 2                   |
| 5ta       | 2    | {name:'Phil' , age: 21, voted: true}   | true          | 3                   |
| 6ta       | 3    | {name:'Ed' , age:55, voted:true}       | true          | 4                   |
| 7ma       | 4    | {name:'Tami' , age: 54, voted:true}    | true          | 5                   |
| 8va       | 5    | {name: 'Mary', age: 31, voted: false}  | false         | 5                   |
| 9na       | 5    | {name: 'Becky', age: 43, voted: false} | false         | 5                   |
| 10ma      | 5    | {name: 'Joey', age: 41, voted: true}   | true          | 6                   |
| 11va      | 6    | {name: 'Jeff', age: 30, voted: true}   | true          | 7                   |
| 12va      | 7    | {name: 'Zack', age: 19, voted: false}  | false         | 7                   |

* En el cuerpo del `reduce` hacemos una condicional ternaria `acc + (current.voted ? 1 : 0)` donde sumamos `1` a `acc` su `current.voted` es `true`, caso contrario le sumamos `0`

* Repetimos esta operación hasta que no queden objetos en el arreglo.

* Recuerda que el valor de `current` en este ejemplo ya no es un primitivo, ahora `current` representa en cada iteración un objeto del arreglo `voters`.

## **Ejemplo #4: Convertir una matriz en un arreglo**

{% stackblitz js-ow85ae %}

| Iteración | acc                   | current         | ...acc              | ...current    | [...acc, ...current]           |
| --------- | --------------------- | --------------- | ------------------- | ------------- | ------------------------------ |
| 1ra       | []                    | ["1", "2", "3"] | []                  | "1", "2", "3" | ["1", "2", "3"]                |
| 2da       | ["1", "2", "3"]       | [true]          | "1", "2", "3"       | true          | ["1", "2", "3", true]          |
| 3ra       | ["1", "2", "3", true] | [4, 5, 6]       | "1", "2", "3", true | 4, 5, 6       | ["1", "2", "3", true, 4, 5, 6] |

* El valor incial de `acc` ya no es un simple valor, ahora `acc` de entrada es un arreglo vacio.
* Usamos `spread operator` para concatenar las copias de los arreglos `acc` y `current` en cada iteración.

## **Ejemplo #5: GroupBy con reduce**
Dado el siguiente arreglo de objetos: 

```js
const voters2 = [
  {name:'Bob' , age: 30, voted: true},
  {name:'Jake' , age: 32, voted: true},
  {name:'Kate' , age: 25, voted: false},
  {name:'Sam' , age: 20, voted: false},
  {name:'Phil' , age: 21, voted: true},
  {name:'Ed' , age:55, voted:true},
  {name:'Tami' , age: 54, voted:true},
  {name: 'Mary', age: 31, voted: false},
  {name: 'Becky', age: 43, voted: false},
  {name: 'Joey', age: 41, voted: true},
  {name: 'Jeff', age: 30, voted: true},
  {name: 'Zack', age: 19, voted: false}
];
```
Agrupar y contar la cantidad de personas de la siguiente manera:
* Cuantas personas jovenes hay (entre 18 y 25 años) y cuantas de estas personas jovenes votaron.
* Cuantas personas adultas hay (entre 26 y 35 años) y cuantas de estas personas adultas votaron.
* Cuantas personas maduras hay (entre 36 y 55 años) y cuantas de estas personas maduras votaron.


{% stackblitz js-2rqupf %}

* El valor inicial de `acc` es un objeto con ciertas propiedades que nos interesan.

* En cada iteracion se evaluan las condiconales y se incementan los valores del objeto. 

* Es importante que la funcion retorne algo puesto que sino lo hacemos tendremos un error, en este ejemplo hacemos `return {...acc}` para regresar una copia del objeto incial sino se cae en las condicionales.

> *Este ejemplo es suficientemente claro tener una tabla explicativa*


## **Ejemplo #6: Promedios con reduce**
Dado el siguiente arreglo :

```js
const nums = [
  [1,6,9,2,5,4],
  [50,67,3,80,24,17],
  [100,77,50,35,12,56]
];
```
Obtener en un nuevo arreglo los promedios de cada uno de ellos.

{% stackblitz js-ph3hpu %}

| Iteración | acc  | current | Resultado parcial | item.length |
| --------- | ---- | ------- | ----------------- | ----------- |
| 1ra       | []   | 1       | 1                 | 6           |
| 2da       | 1    | 6       | 7                 | 6           |
| 3ra       | 7    | 9       | 16                | 6           |
| 4ta       | 16   | 2       | 18                | 6           |
| 5ta       | 18   | 5       | 23                | 6           |
| 6ta       | 23   | 4       | 27                | 6           |

* Usamos un `map` para iterar sobre cada arreglo. En la 1ra iteración nos enfocamos en `[1,6,9,2,5,4]`.
* Obtenemos la sumatoria que es `27` y luego lo dividimos entre el numero de elementos `6`, obteniendo `[4.5]`
* `map` vuelve a iterar y ahora hacemos lo mismo con `[50,67,3,80,24,17]` y luego con `[100,77,50,35,12,56]`
* Resultado final: `[ 4.5, 40.166666666666664, 55 ]`

## **Ejemplo #7: Contador de boleanos**
Dado el arreglo:

```js
const booleans = [true, true, false, true, false, true];
```
Contar cuantos valores `true` hay.

{% stackblitz js-qtzedu %}

| Iteración | acc  | current | Resultado parcial |
| --------- | ---- | ------- | ----------------- |
| 1ra       | 0    | true    | 1                 |
| 2da       | 1    | true    | 2                 |
| 3ra       | 2    | false   | 2                 |
| 4ta       | 2    | true    | 3                 |
| 5ta       | 3    | false   | 3                 |
| 6ta       | 3    | true    | 4                 |

* Recorremos el arreglo y cuando se encuentra un `true` incrementamos en uno el valor de `acc`. Caso contrario no le sumamos nada.

## **Ejemplo #8: Contador mejorado**

{% stackblitz js-qutzz5 %}

Es posible mejorar el ejemplo del contador inicializando el `acc` con un objeto y usando propiedades dinamicas con la sintaxis `obj[propiedad]`.


## **Ejemplo #9: Convertir arreglos a objetos**
Dado un arreglo de objetos:

```js
const personas = [ 
  {nombre:"Cristian", edad:25, sexo:"M"},
  {nombre:"Ana", edad:20, sexo:"F"},
  {nombre:"Fernando", edad:15, sexo:"M"},
  {nombre:"Alejandra", edad:11, sexo:"F"},
];
```
Convertirlo a algo como:

```js
  {
    Cristian: {
      edad:25,
      sexo:"M"
    }
  }
```

{% stackblitz js-ldfnh5 %}

* En cada iteración el valor de `current` sera uno de los obejtos del arreglo empezando por `{nombre:"Cristian", edad:25, sexo:"M"}`
* Para cada `nombre` accedemos a `current.nombre` del objeto `acc` que declaramos como vacio.
* Imprimimos las llaves `sexo` y `edad`.