JavaScript

JavaScript

Tutorial JavaScript: Reducción con reduce()

Aprende a usar el método reduce() en JavaScript para transformar y agregar colecciones en un único valor de forma eficiente y funcional.

Aprende JavaScript y certifícate

Fundamentos de reduce(): Agregación y transformación de colecciones hacia un único valor

El método reduce() es una herramienta poderosa en la programación funcional que permite combinar todos los elementos de un array para producir un único valor resultante. A diferencia de map() que transforma elementos uno a uno, y filter() que selecciona elementos, reduce() nos permite "reducir" o "condensar" toda una colección.

Sintaxis básica

La sintaxis básica de reduce() es la siguiente:

array.reduce(callback(acumulador, valorActual[, índice[, array]]), valorInicial)

Donde:

  • callback: Función que se ejecuta en cada elemento
  • acumulador: Almacena el valor retornado en cada iteración
  • valorActual: El elemento que está siendo procesado
  • índice (opcional): Posición del elemento actual
  • array (opcional): El array sobre el cual se llamó a reduce()
  • valorInicial (opcional): Valor con el que comienza el acumulador

¿Cómo funciona?

Imagina el método reduce() como un proceso paso a paso:

  1. Comienza con un valor inicial (o el primer elemento si no se proporciona valor inicial)
  2. Para cada elemento del array, ejecuta una función que combina el acumulador con el elemento actual
  3. El resultado se convierte en el nuevo acumulador para el siguiente elemento
  4. Al final, devuelve el valor final del acumulador

Ejemplos básicos

Veamos algunos ejemplos fundamentales para entender cómo funciona reduce():

  • 1. Suma de valores en un array:
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
console.log(sum); // 15
  • 2. Calculando el producto de todos los elementos:
const numbers = [1, 2, 3, 4];
const product = numbers.reduce((accumulator, currentValue) => accumulator * currentValue, 1);
console.log(product); // 24
  • 3. Encontrando el valor máximo:
const numbers = [5, 20, 100, 60, 1];
const max = numbers.reduce((max, current) => current > max ? current : max, numbers[0]);
console.log(max); // 100

La importancia del valor inicial

El parámetro initialValue es crucial en muchas operaciones con reduce(). Aunque es opcional, omitirlo puede llevar a comportamientos inesperados:

// Con valor inicial
[].reduce((acc, curr) => acc + curr, 0); // Devuelve 0

// Sin valor inicial
[].reduce((acc, curr) => acc + curr); // Error: Reduce of empty array with no initial value

Proporcionar un valor inicial adecuado tiene varias ventajas:

  • Evita errores con arrays vacíos
  • Define claramente el tipo de dato del resultado
  • Mejora la legibilidad del código
  • Permite transformaciones entre diferentes tipos de datos

Transformación de tipos

Una característica poderosa de reduce() es que el acumulador puede ser de un tipo diferente al de los elementos del array:

// Transformando un array de strings a un objeto
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const count = fruits.reduce((countObj, fruit) => {
  countObj[fruit] = (countObj[fruit] || 0) + 1;
  return countObj;
}, {});

console.log(count); // { apple: 3, banana: 2, orange: 1 }

En este ejemplo, transformamos un array de strings en un objeto que cuenta las ocurrencias de cada fruta.

Conexión con métodos anteriores

reduce() complementa perfectamente a map() y filter() que vimos en lecciones anteriores:

const orders = [
  { id: 1, items: 3, total: 150 },
  { id: 2, items: 1, total: 50 },
  { id: 3, items: 5, total: 200 }
];

// Calcular el total de ventas para pedidos con más de 2 items
const totalForLargeOrders = orders
  .filter(order => order.items > 2)
  .reduce((sum, order) => sum + order.total, 0);

console.log(totalForLargeOrders); // 350

Casos de uso comunes

  • 1. Aplanar arrays anidados:
const nestedArrays = [[1, 2], [3, 4], [5, 6]];
const flattened = nestedArrays.reduce((acc, curr) => acc.concat(curr), []);
console.log(flattened); // [1, 2, 3, 4, 5, 6]
  • 2. Agrupar objetos por una propiedad:
const people = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge = people.reduce((groups, person) => {
  const age = person.age;
  groups[age] = groups[age] || [];
  groups[age].push(person);
  return groups;
}, {});

console.log(groupedByAge);
// {
//   25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
//   30: [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }]
// }
  • 3. Eliminar duplicados de un array:
const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = numbers.reduce((acc, curr) => {
  if (!acc.includes(curr)) {
    acc.push(curr);
  }
  return acc;
}, []);

console.log(unique); // [1, 2, 3, 4, 5]

Consideraciones de rendimiento

Aunque reduce() es muy versátil, hay algunas consideraciones importantes:

  • Para operaciones simples en arrays grandes, los bucles tradicionales pueden ser más rápidos
  • Evita modificar el acumulador directamente (mutación) para mantener los principios de programación funcional
  • Considera usar reduceRight() cuando necesites procesar un array de derecha a izquierda
// Ejemplo de reduceRight() para invertir un string
const string = "Hello";
const reversed = Array.from(string).reduceRight((acc, char) => acc + char, "");
console.log(reversed); // "olleH"

Patrones avanzados de reducción: Más allá de las agregaciones numéricas simples

Aunque comenzamos con ejemplos sencillos como sumas, reduce() es increíblemente versátil. Veamos patrones más elaborados pero prácticos.

Aplanamiento de arrays

Podemos usar reduce() para aplanar arrays anidados:

const nestedArrays = [[1, 2], [3, 4], [5, 6]];
const flattened = nestedArrays.reduce((accumulator, currentArray) => 
  accumulator.concat(currentArray), []);

console.log(flattened); // [1, 2, 3, 4, 5, 6]

Agrupación de objetos por una propiedad

const people = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 },
  { name: 'Dave', age: 30 }
];

const groupedByAge = people.reduce((groups, person) => {
  // Obtener la edad como clave para el grupo
  const age = person.age;
  
  // Si el grupo no existe, crearlo como array vacío
  if (!groups[age]) {
    groups[age] = [];
  }
  
  // Añadir la persona al grupo correspondiente
  groups[age].push(person);
  
  return groups;
}, {});

console.log(groupedByAge);
// {
//   25: [{ name: 'Alice', age: 25 }, { name: 'Charlie', age: 25 }],
//   30: [{ name: 'Bob', age: 30 }, { name: 'Dave', age: 30 }]
// }

Eliminación de duplicados

Siguiendo el principio de inmutabilidad que vimos anteriormente, podemos usar reduce() para eliminar duplicados:

const numbers = [1, 2, 2, 3, 4, 4, 5];
const unique = numbers.reduce((accumulator, current) => {
  // Si el número no está en el acumulador, lo añadimos
  if (!accumulator.includes(current)) {
    return [...accumulator, current]; // Creamos un nuevo array (inmutabilidad)
  }
  return accumulator;
}, []);

console.log(unique); // [1, 2, 3, 4, 5]

Construcción de una estructura de datos compleja

Podemos usar reduce() para construir estructuras más complejas a partir de datos simples:

const sales = [
  { product: 'Laptop', category: 'Electronics', price: 1200 },
  { product: 'Shirt', category: 'Clothing', price: 25 },
  { product: 'Headphones', category: 'Electronics', price: 80 },
  { product: 'Pants', category: 'Clothing', price: 50 }
];

const summary = sales.reduce((result, item) => {
  // Actualizar ventas totales
  result.total += item.price;
  
  // Actualizar ventas por categoría
  if (!result.byCategory[item.category]) {
    result.byCategory[item.category] = 0;
  }
  result.byCategory[item.category] += item.price;
  
  // Actualizar cantidad de productos
  result.productCount += 1;
  
  return result;
}, { 
  total: 0, 
  byCategory: {}, 
  productCount: 0 
});

console.log(summary);
// {
//   total: 1355,
//   byCategory: { Electronics: 1280, Clothing: 75 },
//   productCount: 4
// }

Procesamiento de datos en cadena

Usando los principios de inmutabilidad que vimos anteriormente, podemos procesar datos en múltiples pasos:

const data = [
  { id: 1, name: 'Alice', active: true, points: 100 },
  { id: 2, name: 'Bob', active: false, points: 200 },
  { id: 3, name: 'Charlie', active: true, points: 150 }
];

// Función para procesar datos en varias etapas
function processData(data) {
  return data.reduce((result, user) => {
    // Solo incluir usuarios activos (como haríamos con filter)
    if (!user.active) return result;
    
    // Transformar el formato (como haríamos con map)
    const formattedUser = {
      id: user.id,
      name: user.name.toUpperCase(),
      level: user.points > 100 ? 'Advanced' : 'Beginner'
    };
    
    // Añadir al resultado (mantiene inmutabilidad)
    return [...result, formattedUser];
  }, []);
}

console.log(processData(data));
// [
//   { id: 1, name: 'ALICE', level: 'Beginner' },
//   { id: 3, name: 'CHARLIE', level: 'Advanced' }
// ]

Este ejemplo muestra cómo reduce() puede combinar operaciones que normalmente harías con filter() y map() en un solo paso, manteniendo los principios de inmutabilidad.

Optimización y composición: Estrategias para reducciones eficientes en contextos funcionales

La optimización de operaciones reduce() va más allá de simplemente escribir código que funcione. En entornos de producción con grandes volúmenes de datos o en sistemas con restricciones de rendimiento, implementar estrategias eficientes puede marcar una diferencia significativa. Veamos cómo podemos mejorar nuestras reducciones y componerlas de manera óptima.

Selección del valor inicial adecuado

La elección del valor inicial no solo afecta la corrección del resultado, sino también el rendimiento:

// Menos eficiente: conversión innecesaria en cada iteración
const sum = [1, 2, 3, 4, 5].reduce((acc, num) => Number(acc) + Number(num));

// Más eficiente: tipo de dato consistente desde el inicio
const optimizedSum = [1, 2, 3, 4, 5].reduce((acc, num) => acc + num, 0);

Cuando el valor inicial coincide con el tipo de resultado esperado, evitamos conversiones de tipo innecesarias en cada iteración.

Implementación de funciones reductoras reutilizables

Podemos crear funciones reductoras reutilizables siguiendo los principios de funciones puras que vimos anteriormente:

// Reductores reutilizables
const sumReducer = (acc, val) => acc + val;
const countReducer = (acc, _) => acc + 1;
const maxReducer = (acc, val) => Math.max(acc, val);
const minReducer = (acc, val) => Math.min(acc, val);

// Uso
const numbers = [5, 10, 15, 20, 25];
const sum = numbers.reduce(sumReducer, 0);          // 75
const count = numbers.reduce(countReducer, 0);      // 5
const max = numbers.reduce(maxReducer, -Infinity);  // 25
const min = numbers.reduce(minReducer, Infinity);   // 5

Encapsulamiento de lógica compleja

Cuando la lógica de reducción es compleja, es recomendable encapsularla en funciones nombradas:

// Función para agrupar por una propiedad
function groupBy(array, property) {
  return array.reduce((groups, item) => {
    const value = item[property];
    groups[value] = groups[value] || [];
    groups[value].push(item);
    return groups;
  }, {});
}

// Uso
const students = [
  { id: 1, name: 'Alice', course: 'JavaScript' },
  { id: 2, name: 'Bob', course: 'Python' },
  { id: 3, name: 'Charlie', course: 'JavaScript' }
];

const studentsByCourse = groupBy(students, 'course');
console.log(studentsByCourse);
// {
//   JavaScript: [{ id: 1, name: 'Alice', ... }, { id: 3, name: 'Charlie', ... }],
//   Python: [{ id: 2, name: 'Bob', ... }]
// }

Combinación de operaciones en un solo reduce

Cuando trabajamos con grandes conjuntos de datos, a veces es más eficiente combinar varias operaciones en un solo reduce():

const transactions = [
  { id: 1, amount: 100, type: 'income' },
  { id: 2, amount: 50, type: 'expense' },
  { id: 3, amount: 200, type: 'income' },
  { id: 4, amount: 30, type: 'expense' }
];

// Múltiples pasadas (menos eficiente para grandes datasets)
const incomes = transactions.filter(t => t.type === 'income');
const totalIncome = incomes.reduce((total, t) => total + t.amount, 0);
const expenses = transactions.filter(t => t.type === 'expense');
const totalExpenses = expenses.reduce((total, t) => total + t.amount, 0);

// Una sola pasada (más eficiente)
const balance = transactions.reduce((result, transaction) => {
  if (transaction.type === 'income') {
    result.income += transaction.amount;
  } else if (transaction.type === 'expense') {
    result.expenses += transaction.amount;
  }
  result.balance = result.income - result.expenses;
  return result;
}, { income: 0, expenses: 0, balance: 0 });

console.log(balance);
// { income: 300, expenses: 80, balance: 220 }

Depuración de reducción

Para depurar operaciones complejas de reduce(), podemos usar console.log dentro de la función:

const result = [1, 2, 3, 4].reduce((acc, curr, idx) => {
  console.log(`Iteration ${idx}: acc = ${acc}, curr = ${curr}`);
  return acc + curr;
}, 0);

// Muestra:
// Iteration 0: acc = 0, curr = 1
// Iteration 1: acc = 1, curr = 2
// Iteration 2: acc = 3, curr = 3
// Iteration 3: acc = 6, curr = 4
// Resultado final: 10

Balance entre legibilidad y rendimiento

A veces, es mejor priorizar la legibilidad sobre pequeñas optimizaciones:

// Más legible y mantenible
function processTransactions(transactions) {
  // Filtrar primero
  const validTransactions = transactions.filter(t => t.status === 'completed');
  
  // Luego mapear para transformar
  const amounts = validTransactions.map(t => t.amount);
  
  // Finalmente reducir
  return amounts.reduce((total, amount) => total + amount, 0);
}

// Versus versión más optimizada pero menos clara
function optimizedProcessTransactions(transactions) {
  return transactions.reduce((total, t) => 
    t.status === 'completed' ? total + t.amount : total, 0);
}

En la mayoría de los casos, la primera versión es preferible por su claridad, a menos que estés trabajando con conjuntos de datos masivos donde el rendimiento es crítico.

El método reduce() es una herramienta extremadamente poderosa en programación funcional que nos permite:

  • Transformar colecciones en valores únicos de cualquier tipo
  • Implementar agregaciones complejas y transformaciones de datos
  • Combinar varias operaciones en un solo paso
  • Construir estructuras de datos complejas a partir de datos simples

Hemos visto cómo reduce() complementa a map() y filter(), y cómo juntos forman la base de la programación funcional en JavaScript. Siguiendo los principios de inmutabilidad y funciones puras que vimos en lecciones anteriores, reduce() nos permite escribir código más declarativo, mantenible y eficiente.

Aprende JavaScript online

Otros ejercicios de programación de JavaScript

Evalúa tus conocimientos de esta lección Reducción con reduce() con nuestros retos de programación de tipo Test, Puzzle, Código y Proyecto con VSCode, guiados por IA.

Clases y objetos

JavaScript
Código

Uso de operadores

JavaScript
Puzzle

Uso de operadores

JavaScript
Test

Estructuras de control

JavaScript
Test

Proyecto Manipulación DOM

JavaScript
Proyecto

Excepciones

JavaScript
Test

Transformación con map()

JavaScript
Código

Arrays y Métodos

JavaScript
Código

Reto Métodos de Strings

JavaScript
Código

Transformación con map()

JavaScript
Puzzle

Funciones flecha

JavaScript
Test

Async / Await

JavaScript
Código

Selección de elementos DOM

JavaScript
Puzzle

API Fetch

JavaScript
Código

Encapsulación

JavaScript
Test

Mapas con Map

JavaScript
Código

Creación y uso de variables

JavaScript
Puzzle

Polimorfismo

JavaScript
Puzzle

Reto Funciones flecha

JavaScript
Código

Tipos de datos

JavaScript
Puzzle

Reto Operadores avanzados

JavaScript
Código

Promises

JavaScript
Código

Reto Estructuras de control

JavaScript
Código

Estructuras de control

JavaScript
Puzzle

Pruebas unitarias

JavaScript
Proyecto

Inmutabilidad y programación funcional pura

JavaScript
Código

Funciones flecha

JavaScript
Puzzle

Polimorfismo

JavaScript
Test

Reto Polimorfismo

JavaScript
Código

Array

JavaScript
Código

Transformación con map()

JavaScript
Test

Reto Variables

JavaScript
Código

Gestor de tareas con JavaScript

JavaScript
Proyecto

Proyecto Modificación de elementos DOM

JavaScript
Proyecto

Manipulación DOM

JavaScript
Test

Funciones

JavaScript
Test

Conjuntos con Set

JavaScript
Código

Reto Prototipos y cadena de prototipos

JavaScript
Código

Reto Encapsulación

JavaScript
Código

Funciones flecha

JavaScript
Código

Async / Await

JavaScript
Código

Reto Excepciones

JavaScript
Código

Reto Filtrado con filter() y find()

JavaScript
Código

Creación y uso de variables

JavaScript
Test

Excepciones

JavaScript
Puzzle

Promises

JavaScript
Código

Funciones cierre (closure)

JavaScript
Test

Reto Herencia

JavaScript
Código

Herencia

JavaScript
Puzzle

Proyecto Eventos del DOM

JavaScript
Proyecto

Herencia

JavaScript
Test

Selección de elementos DOM

JavaScript
Test

Modificación de elementos DOM

JavaScript
Test

Reto Clases y objetos

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Test

Funciones cierre (closure)

JavaScript
Puzzle

Reto Destructuring de objetos y arrays

JavaScript
Código

Callbacks

JavaScript
Código

Funciones

JavaScript
Puzzle

Mapas con Map

JavaScript
Test

Reducción con reduce()

JavaScript
Test

Callbacks

JavaScript
Puzzle

Manipulación DOM

JavaScript
Puzzle

Introducción al DOM

JavaScript
Proyecto

Reto Funciones

JavaScript
Código

Reto Funciones cierre (closure)

JavaScript
Código

Promises

JavaScript
Test

Reto Reducción con reduce()

JavaScript
Código

Async / Await

JavaScript
Test

Reto Estructuras de control

JavaScript
Código

Eventos del DOM

JavaScript
Puzzle

Introducción a JavaScript

JavaScript
Puzzle

Async / Await

JavaScript
Puzzle

Promises

JavaScript
Puzzle

Selección de elementos DOM

JavaScript
Proyecto

Filtrado con filter() y find()

JavaScript
Código

Callbacks

JavaScript
Test

Creación de clases y objetos Restaurante

JavaScript
Código

Reducción con reduce()

JavaScript
Código

Filtrado con filter() y find()

JavaScript
Puzzle

Reducción con reduce()

JavaScript
Puzzle

Conjuntos con Set

JavaScript
Puzzle

Herencia de clases

JavaScript
Código

Eventos del DOM

JavaScript
Test

Clases y objetos

JavaScript
Puzzle

Modificación de elementos DOM

JavaScript
Puzzle

Mapas con Map

JavaScript
Puzzle

Proyecto carrito compra agoodshop

JavaScript
Proyecto

Introducción a JavaScript

JavaScript
Test

Reto Mapas con Map

JavaScript
Código

Funciones

JavaScript
Código

Proyecto administrador de contactos

JavaScript
Proyecto

Reto Expresiones regulares

JavaScript
Código

Tipos de datos

JavaScript
Test

Clases y objetos

JavaScript
Test

Array

JavaScript
Test

Conjuntos con Set

JavaScript
Test

Array

JavaScript
Puzzle

Encapsulación

JavaScript
Puzzle

Todas las lecciones de JavaScript

Accede a todas las lecciones de JavaScript y aprende con ejemplos prácticos de código y ejercicios de programación con IDE web sin instalar nada.

Introducción A Javascript

JavaScript

Introducción Y Entorno

Tipos De Datos

JavaScript

Sintaxis

Variables

JavaScript

Sintaxis

Operadores

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Métodos De Strings

JavaScript

Sintaxis

Funciones Cierre (Closure)

JavaScript

Sintaxis

Operadores Avanzados

JavaScript

Sintaxis

Funciones

JavaScript

Sintaxis

Expresiones Regulares

JavaScript

Sintaxis

Estructuras De Control

JavaScript

Sintaxis

Arrays Y Métodos

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Mapas Con Map

JavaScript

Estructuras De Datos

Conjuntos Con Set

JavaScript

Estructuras De Datos

Funciones Flecha

JavaScript

Programación Funcional

Filtrado Con Filter() Y Find()

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Reducción Con Reduce()

JavaScript

Programación Funcional

Funciones Flecha

JavaScript

Programación Funcional

Transformación Con Map()

JavaScript

Programación Funcional

Inmutabilidad Y Programación Funcional Pura

JavaScript

Programación Funcional

Clases Y Objetos

JavaScript

Programación Orientada A Objetos

Excepciones

JavaScript

Programación Orientada A Objetos

Encapsulación

JavaScript

Programación Orientada A Objetos

Herencia

JavaScript

Programación Orientada A Objetos

Polimorfismo

JavaScript

Programación Orientada A Objetos

This Y Contexto

JavaScript

Programación Orientada A Objetos

Patrón De Módulos Y Namespace

JavaScript

Programación Orientada A Objetos

Prototipos Y Cadena De Prototipos

JavaScript

Programación Orientada A Objetos

Destructuring De Objetos Y Arrays

JavaScript

Programación Orientada A Objetos

Manipulación Dom

JavaScript

Dom

Selección De Elementos Dom

JavaScript

Dom

Modificación De Elementos Dom

JavaScript

Dom

Eventos Del Dom

JavaScript

Dom

Localstorage Y Sessionstorage

JavaScript

Dom

Bom (Browser Object Model)

JavaScript

Dom

Callbacks

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Promises

JavaScript

Programación Asíncrona

Api Fetch

JavaScript

Programación Asíncrona

Async / Await

JavaScript

Programación Asíncrona

Naturaleza De Js Y Event Loop

JavaScript

Programación Asíncrona

Callbacks

JavaScript

Programación Asíncrona

Websockets

JavaScript

Programación Asíncrona

Módulos En Es6

JavaScript

Construcción

Configuración De Bundlers Como Vite

JavaScript

Construcción

Eslint Y Calidad De Código

JavaScript

Construcción

Npm Y Dependencias

JavaScript

Construcción

Introducción A Pruebas En Js

JavaScript

Testing

Pruebas Unitarias

JavaScript

Testing

Accede GRATIS a JavaScript y certifícate

En esta lección

Objetivos de aprendizaje de esta lección

  • Entender el propósito y uso del método reduce() en JavaScript.
  • Conocer la sintaxis del método reduce() y cómo se aplica a un array.
  • Aprender a definir funciones reductoras que operen sobre los elementos del array y acumulen resultados.
  • Comprender el concepto de acumulador y cómo se utiliza en la función reductora.
  • Saber cómo proporcionar un valor inicial para el acumulador y cómo afecta el proceso de reducción.
  • Conocer ejemplos prácticos de uso del método reduce() para calcular sumas, productos, promedios y conteos de elementos en un array.
  • Comprender la flexibilidad y utilidad del método reduce() para adaptarse a diferentes casos de uso en el procesamiento de arrays.