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ícateFundamentos 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 elementoacumulador
: Almacena el valor retornado en cada iteraciónvalorActual
: El elemento que está siendo procesadoíndice
(opcional): Posición del elemento actualarray
(opcional): El array sobre el cual se llamó areduce()
valorInicial
(opcional): Valor con el que comienza el acumulador
¿Cómo funciona?
Imagina el método reduce()
como un proceso paso a paso:
- Comienza con un valor inicial (o el primer elemento si no se proporciona valor inicial)
- Para cada elemento del array, ejecuta una función que combina el acumulador con el elemento actual
- El resultado se convierte en el nuevo acumulador para el siguiente elemento
- 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.
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
Uso de operadores
Uso de operadores
Estructuras de control
Proyecto Manipulación DOM
Excepciones
Transformación con map()
Arrays y Métodos
Reto Métodos de Strings
Transformación con map()
Funciones flecha
Async / Await
Selección de elementos DOM
API Fetch
Encapsulación
Mapas con Map
Creación y uso de variables
Polimorfismo
Reto Funciones flecha
Tipos de datos
Reto Operadores avanzados
Promises
Reto Estructuras de control
Estructuras de control
Pruebas unitarias
Inmutabilidad y programación funcional pura
Funciones flecha
Polimorfismo
Reto Polimorfismo
Array
Transformación con map()
Reto Variables
Gestor de tareas con JavaScript
Proyecto Modificación de elementos DOM
Manipulación DOM
Funciones
Conjuntos con Set
Reto Prototipos y cadena de prototipos
Reto Encapsulación
Funciones flecha
Async / Await
Reto Excepciones
Reto Filtrado con filter() y find()
Creación y uso de variables
Excepciones
Promises
Funciones cierre (closure)
Reto Herencia
Herencia
Proyecto Eventos del DOM
Herencia
Selección de elementos DOM
Modificación de elementos DOM
Reto Clases y objetos
Filtrado con filter() y find()
Funciones cierre (closure)
Reto Destructuring de objetos y arrays
Callbacks
Funciones
Mapas con Map
Reducción con reduce()
Callbacks
Manipulación DOM
Introducción al DOM
Reto Funciones
Reto Funciones cierre (closure)
Promises
Reto Reducción con reduce()
Async / Await
Reto Estructuras de control
Eventos del DOM
Introducción a JavaScript
Async / Await
Promises
Selección de elementos DOM
Filtrado con filter() y find()
Callbacks
Creación de clases y objetos Restaurante
Reducción con reduce()
Filtrado con filter() y find()
Reducción con reduce()
Conjuntos con Set
Herencia de clases
Eventos del DOM
Clases y objetos
Modificación de elementos DOM
Mapas con Map
Proyecto carrito compra agoodshop
Introducción a JavaScript
Reto Mapas con Map
Funciones
Proyecto administrador de contactos
Reto Expresiones regulares
Tipos de datos
Clases y objetos
Array
Conjuntos con Set
Array
Encapsulación
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
Introducción Y Entorno
Tipos De Datos
Sintaxis
Variables
Sintaxis
Operadores
Sintaxis
Estructuras De Control
Sintaxis
Funciones
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Métodos De Strings
Sintaxis
Funciones Cierre (Closure)
Sintaxis
Operadores Avanzados
Sintaxis
Funciones
Sintaxis
Expresiones Regulares
Sintaxis
Estructuras De Control
Sintaxis
Arrays Y Métodos
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Mapas Con Map
Estructuras De Datos
Conjuntos Con Set
Estructuras De Datos
Funciones Flecha
Programación Funcional
Filtrado Con Filter() Y Find()
Programación Funcional
Transformación Con Map()
Programación Funcional
Reducción Con Reduce()
Programación Funcional
Funciones Flecha
Programación Funcional
Transformación Con Map()
Programación Funcional
Inmutabilidad Y Programación Funcional Pura
Programación Funcional
Clases Y Objetos
Programación Orientada A Objetos
Excepciones
Programación Orientada A Objetos
Encapsulación
Programación Orientada A Objetos
Herencia
Programación Orientada A Objetos
Polimorfismo
Programación Orientada A Objetos
This Y Contexto
Programación Orientada A Objetos
Patrón De Módulos Y Namespace
Programación Orientada A Objetos
Prototipos Y Cadena De Prototipos
Programación Orientada A Objetos
Destructuring De Objetos Y Arrays
Programación Orientada A Objetos
Manipulación Dom
Dom
Selección De Elementos Dom
Dom
Modificación De Elementos Dom
Dom
Eventos Del Dom
Dom
Localstorage Y Sessionstorage
Dom
Bom (Browser Object Model)
Dom
Callbacks
Programación Asíncrona
Promises
Programación Asíncrona
Async / Await
Programación Asíncrona
Promises
Programación Asíncrona
Api Fetch
Programación Asíncrona
Async / Await
Programación Asíncrona
Naturaleza De Js Y Event Loop
Programación Asíncrona
Callbacks
Programación Asíncrona
Websockets
Programación Asíncrona
Módulos En Es6
Construcción
Configuración De Bundlers Como Vite
Construcción
Eslint Y Calidad De Código
Construcción
Npm Y Dependencias
Construcción
Introducción A Pruebas En Js
Testing
Pruebas Unitarias
Testing
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.