🖖 Inicio

SERVICIOS

Salir

nochedía

Cómo funciona Array.reduce() en JavaScript

600 palabras
3 minutos
June 11, 2019
blogjavascript

Te enseño como utilizar Array.reduce() de JavaScript y para qué sirve

  1. Prólogo
  2. Uso básico
  3. Uso completo
  4. Resumen

Prólogo

La función reduce para arrays es una función muy potente que sin embargo resulta ofuscada hasta que no te acostumbras a su uso

En esta entrada te hablo de reduce y de los distintos usos que puede tener

Uso básico

El objetivo de reduce es sencillo y el nombre lo que te sugiere es que vas a reducir un array a un único valor, esto es a partir de los elementos del array

El funcionamiento es un tanto complejo:

js
Array.reduce((accumulator, currentValue) => { }, initialValue)

reduce recibe por lo tanto una función, donde accumulator es el elemento que irá sumando o agrupando lo que queramos y que al final será retornado, es el elemento clave de la función y es el nuevo objeto | array | valor | nueva estructura que se generará, mientras que currentValue es el elemento del array que estaremos analizando

Y fuera de la función, initialValue es opcional

  • si no lo tenemos, accumulator será el primer elemento del array y currentValue el segundo elemento
  • si sí lo especificamos, entonces accumulator será ese valor y currentValue será el primer elemento del array

Por lo tanto es importante especificar el valor de initialValue, aunque sea cero, para que currentValue pase por todos los elementos del array y no se salte el primero

El ejemplo más simple de reduce es el de coger un array como const myArr = [1,2,3,4] y utilizar reduce para retornar la suma de todos los elementos

js
const myArr = [1,2,3,4]
const sum = myArr.reduce((accumulator, currentValue) => {
return accumulator + currentValue
}, 0)
console.log(sum) // 10

Uso completo

Pero pocas veces utilizarás reduce en su expresión más literal. El uso más habitual es utilizar reduce para evaluar un array y sacar valores en función de ese array, que no tiene porqué ser un único valor numérico, puede ser un nuevo array o un nuevo object

Con estos usos el nombre de reduce pierde algo de sentido, aunque sigue representando el hecho de que estamos reduciendo una estructura de datos a otra estructura de datos más simple, lo cierto es que la nueva estructura puede ser más compleja, depende de nosotros

Por ejemplo, voy a contar de un objeto el número de veces que tenemos elementos con unas características dadas

En este caso elementos, quiero contar cuantos elementos tienen el type de laptop y la location de barcelona

js
let myArr = [
{
site: 'https://www.kuworking.com/',
data: {
type: 'programming',
location: 'barcelona',
},
},
{
site: 'https://www.kuworking2.com/',
data: {
type: 'laptop',
location: 'barcelona',
},
},
]
let laptop_items = 0
let barcelona_items = 0
// cómo lo haríamos con un loop for..of
for (const el of myArr) {
if (el.data.type === 'laptop') laptop_items++
if (el.data.location === 'barcelona') barcelona_items++
}
console.log(laptop_items) // 1
console.log(barcelona_items) // 2
const laptop_items_reduce = myArr.reduce((accumulator, el) => {
if (el.data.type === 'laptop') accumulator++
return accumulator
}, 0)
const barcelona_items_reduce = myArr.reduce((accumulator, el) => {
if (el.data.location === 'barcelona') accumulator++
return accumulator
}, 0)
console.log(laptop_items_reduce) // 1
console.log(barcelona_items_reduce) // 2

Fíjate que como valor inicial especifíco una nueva variable con 0, necesito especificar el valor inicial ya que sino el accumulator cogería como valor inicial el primer elemento del array

Lo cierto es que escrito así, el uso del for..of es mucho menos ofuscado de leer, aunque su estructura sea imperativa y no declarativa

Pero no es menos cierto que cuando uno acostumbra el ojo al reduce las dos formas quedan igual de naturales, con la ventaja para el reduce de ser declarativo (esto es, te permite entender primero qué hace antes de cómo lo hace)

Podemos optimizar el código para escribir menos y recibir un objeto que agrupe las variables anteriores, o de hecho todas las variables de interés

js
let myArr = [
{
site: 'https://www.kuworking.com/',
data: {
type: 'programming',
location: 'barcelona',
},
},
{
site: 'https://www.kuworking2.com/',
data: {
type: 'laptop',
location: 'barcelona',
},
},
]
// con la estructura for..of
const items_reduce_forof = {}
for (let el of myArr) {
if (el.data.type in items_reduce_forof) items_reduce_forof[el.data.type]++
else items_reduce_forof[el.data.type] = 1
if (el.data.location in items_reduce_forof) items_reduce_forof[el.data.location]++
else items_reduce_forof[el.data.location] = 1
}
// con la estructura reduce
const items_reduce = myArr.reduce((accumulator, el) => {
if (el.data.type in accumulator) accumulator[el.data.type]++
else accumulator[el.data.type] = 1
if (el.data.location in accumulator) accumulator[el.data.location]++
else accumulator[el.data.location] = 1
return accumulator
}, {})
console.log(items_reduce_forof)
console.log(items_reduce)
/*
{
"programming": 1,
"barcelona": 2,
"laptop": 1
}
{
"programming": 1,
"barcelona": 2,
"laptop": 1
}
*/

Y se puede compactar más el código

js
const items_reduce_forof = {}
for (let el of myArr) {
items_reduce_forof[el.data.type] = (items_reduce_forof[el.data.type] || 0 ) +1
items_reduce_forof[el.data.location] = (items_reduce_forof[el.data.location] || 0 ) +1
}
const items_reduce = myArr.reduce((accumulator, el) => {
accumulator[el.data.type] = (accumulator[el.data.type] || 0) +1
accumulator[el.data.location] = (accumulator[el.data.location] || 0) +1
return accumulator
}, {})

O hacerlo para todos los elementos de data (y ahorrando paréntesis siempre que se pueda)

js
const items_reduce_forof = {}
for (let el of myArr)
for (let el2 of Object.values(el.data))
items_reduce_forof[el2] = (items_reduce_forof[el2] || 0 ) +1
const items_reduce = myArr.reduce((accumulator, el) => {
Object.values(el.data).map(el2 => accumulator[el2] = (accumulator[el2] || 0) +1)
return accumulator
}, {})

O ya puestos escoger map para todo

js
const items_reduce_forof = {}
myArr.map(el => Object.values(el.data).map(el2 => items_reduce_forof[el2] = (items_reduce_forof[el2] || 0) +1))

No queda claro qué opción es la mejor, en mi opinión la estructura for es fácil de leer pero te obliga a entender toda la estructura (el cómo) antes de entender qué se está haciendo (y porqué)

Con reduce podemos obviar el cómo ya que rápidamente se entiende que items_reduce es una versión modificada de myArr, sin tener que entender nada más

Con todo con map la lectura es aún más ofuscada y sigue siendo imperativa, pero con el ojo acostumbrado consigues una síntesis significativa

Por lo tanto, al final dependerá de nosotros qué queremos escoger

Resumen

La función reduce nos permite escribir de forma más declarativa, pero sin un periodo de "práctica" su uso puede parecer ofuscado y menos claro que utilizar alternativas más simples en su estructura

Vale la pena el esfuerzo de integrar reduce en el arsenal de programación propio ya que habitualmente nos permitirá escribir de una forma más clara y directa

Además, así preparamos el terreno para el pipeline, lo que se conoce como utilizar el reduce con un array de funciones, algo que queda para otra entrada

Puedes repasar todo el codepen de aquí abajo

Qué tal la entrada?
👌 Bien 🙌🙌
👍 Bien, pero algunas cosas podrían explicarse mejor 😬
🤷‍♂️ Da por sentadas demasiadas cosas 😒
🤷‍♂️ A ver, hay poca chicha 😬
🤷‍♂️ Los ejemplos no son muy claros 🙇‍♂️
🤷‍♂️ No se entiende, está mal escrito 👎
✍️ Hay errores, revísalo en cuanto puedas 🙏

Newsletter de kuworking, un correo de tanto en cuanto

Quizá te interese

Privacidad
by kuworking.com
[ 2020 >> kuworking ]