reduce y agrupación en JavaScript | Nicolás Garzón
reduce recorre una colección y combina sus elementos dentro de un acumulador.
JavaScript
Copiar const total = prices. reduce (
( sum, price ) => sum + price,
0 ,
) ; JavaScript
Copiar const result = values. reduce (
( accumulator, currentValue, index, array ) => {
return nextAccumulator;
} ,
initialValue,
) ;
accumulator contiene el resultado anterior.
currentValue es el elemento actual.
El retorno se convierte en el acumulador siguiente.
Al terminar, el último acumulador es el resultado.
JavaScript
Copiar const total = [ 10 , 20 , 30 ] . reduce (
( sum, number ) => sum + number,
0 ,
) ;
Texto
Copiar 0 + 10 = 10
10 + 20 = 30
30 + 30 = 60JavaScript
Copiar const total = values. reduce (
( sum, value ) => sum + value,
0 ,
) ;
Define el tipo inicial del acumulador.
Hace seguro el array vacío.
Comunica la identidad de la suma.
Sin valor inicial, el primer elemento se usa como acumulador y el recorrido comienza en el segundo.
JavaScript
Copiar [ 10 , 20 , 30 ] . reduce (
( sum, value ) => sum + value,
) ; Sobre un array vacío lanza TypeError.
JavaScript
Copiar [ ] . reduce ( ( sum, value ) => sum + value) ; Incluye normalmente un valor inicial explícito.
JavaScript
Copiar const counts = words. reduce (
( result, word ) => {
result[ word] = ( result[ word] ?? 0 ) + 1 ;
return result;
} ,
Object. create ( null ) ,
) ; Aquí el acumulador se muta localmente y se retorna la misma referencia. Puede ser claro porque el objeto no se comparte durante la construcción.
JavaScript
Copiar const counts = words. reduce (
( result, word ) => {
result. set (
word,
( result. get ( word) ?? 0 ) + 1 ,
) ;
return result;
} ,
new Map ( ) ,
) ; JavaScript
Copiar const productsById = products. reduce (
( result, product ) => {
result[ product. id] = product;
return result;
} ,
Object. create ( null ) ,
) ; JavaScript
Copiar const productsById = new Map (
products. map ( ( product ) => [ product. id, product] ) ,
) ; La segunda opción puede expresar mejor una colección clave-valor.
JavaScript
Copiar const byStatus = orders. reduce (
( groups, order ) => {
const group = groups[ order. status] ?? [ ] ;
group. push ( order) ;
groups[ order. status] = group;
return groups;
} ,
Object. create ( null ) ,
) ; Es válido, pero JavaScript ofrece APIs específicas para agrupación.
JavaScript
Copiar const byStatus = Object. groupBy (
orders,
( order ) => order. status,
) ; Retorna un objeto con prototipo null. Las claves producidas se convierten en property keys, normalmente strings o symbols.
JavaScript
Copiar byStatus. completed; Es apropiado cuando los grupos se consultarán mediante nombres de propiedad.
JavaScript
Copiar const byCustomer = Map. groupBy (
orders,
( order ) => order. customer,
) ; La clave puede ser cualquier valor, incluidos objetos.
JavaScript
Copiar byCustomer. get ( customer) ; Utiliza Map.groupBy cuando la identidad de la clave no debe convertirse a string.
Object.groupBy y Map.groupBy forman parte del estándar desde ECMAScript 2024.
Recorre desde el final hacia el inicio.
JavaScript
Copiar const text = [ "A" , "B" , "C" ] . reduceRight (
( result, value ) => result + value,
"" ,
) ;
Puede ser útil para composición de funciones o estructuras donde el orden derecho-izquierdo tiene significado. No lo uses solo para evitar una operación más clara.
JavaScript
Copiar const flat = nested. reduce (
( result, values ) => result. concat ( values) ,
[ ] ,
) ; flat() comunica mejor el propósito para arrays anidados.
JavaScript
Copiar const flat = nested. flat ( ) ; JavaScript
Copiar const pipeline = functions. reduce (
( value, operation ) => operation ( value) ,
input,
) ; El acumulador cambia de valor después de cada operación.
Este patrón puede ser útil cuando la secuencia es dinámica; una llamada directa puede ser más legible para pocas funciones fijas.
JavaScript
Copiar const result = products. reduce (
( state, product ) => {
if ( product. isActive) {
state. items. push ( transform ( product) ) ;
state. total += product. price;
}
return state;
} ,
{ items : [ ] , total : 0 } ,
) ; Tal vez sea más claro separar las preguntas.
JavaScript
Copiar const activeProducts = products. filter ( isActive) ;
const items = activeProducts. map ( transform) ;
const total = activeProducts. reduce ( sumPrice, 0 ) ; O usar un bucle si necesitas producir varios resultados en un solo recorrido y eso es importante.
JavaScript
Copiar const result = values. reduce (
( items, value ) => [ ... items, transform ( value) ] ,
[ ] ,
) ; Crea un array nuevo en cada iteración y puede tener un costo innecesario.
Mutación local del acumulador:
JavaScript
Copiar const result = values. reduce (
( items, value ) => {
items. push ( transform ( value) ) ;
return items;
} ,
[ ] ,
) ; Como items se creó dentro de la operación y no se comparte, esta mutación puede ser razonable. Aun así, map sería más claro si siempre existe una salida por elemento.
La suma normal con Numbers puede acumular errores de punto flotante.
JavaScript
Copiar [ 0.1 , 0.2 , 0.3 ] . reduce (
( sum, value ) => sum + value,
0 ,
) ; ECMAScript 2026 incorpora Math.sumPrecise, que suma un iterable de Numbers minimizando pérdida de precisión en magnitudes diferentes.
JavaScript
Copiar const total = Math. sumPrecise ( values) ; Verifica soporte en el runtime objetivo antes de utilizar APIs recientes.
Para dinero, la representación del dominio sigue siendo importante: enteros en la unidad mínima, decimal especializado o reglas explícitas.
reduce no espera automáticamente el acumulador ni el callback.
Puede encadenar promesas de forma intencional:
JavaScript
Copiar await tasks. reduce (
async ( previous, task ) => {
await previous;
await runTask ( task) ;
} ,
Promise. resolve ( ) ,
) ; Sin embargo, un for...of suele comunicar mejor una secuencia asíncrona.
JavaScript
Copiar for ( const task of tasks) {
await runTask ( task) ;
} reduce omite holes. Si no existe valor inicial y no encuentra ningún elemento presente, lanza TypeError.
JavaScript
Copiar const summary = products. reduce (
( result, product ) => {
result. totalProducts++ ;
result. totalUnits += product. stock;
result. totalValue +=
product. stock * product. purchaseCost;
return result;
} ,
{
totalProducts : 0 ,
totalUnits : 0 ,
totalValue : 0 ,
} ,
) ; El acumulador representa un único resumen con varias métricas relacionadas.
Omitir el valor inicial sin considerar arrays vacíos.
Olvidar retornar el acumulador siguiente.
Usar reduce para una transformación que map expresa mejor.
Crear una copia completa del acumulador en cada iteración sin necesidad.
Agrupar manualmente sin considerar Object.groupBy o Map.groupBy.
Usar un objeto normal con claves externas sin validar.
Ocultar demasiadas responsabilidades dentro de un solo callback.
Esperar que reduce espere promesas automáticamente.
reduce combina una secuencia dentro de un acumulador.
El retorno de cada callback se usa en la iteración siguiente.
Un valor inicial explícito evita errores con arrays vacíos.
El acumulador puede ser cualquier tipo de valor.
Mutar un acumulador local puede ser razonable.
Object.groupBy agrupa mediante property keys.
Map.groupBy conserva claves de cualquier tipo.
No todo procesamiento necesita reduce.
¿Qué ocurre con este código?
JavaScript
Copiar const total = [ ] . reduce (
( sum, value ) => sum + value,
) ; Respuesta Lanza TypeError porque no existe un valor inicial ni un primer elemento que pueda utilizarse como acumulador. Con 0 como segundo argumento devolvería 0.
Ordenamiento de arrays explica comparadores, estabilidad, mutación y criterios múltiples.