map(), filter() y reduce() son los tres métodos de array más importantes para trabajar con datos de forma funcional en JavaScript. Transforman, filtran y reducen arrays sin mutar el original ni necesitar bucles imperativas. Dominarlos permite escribir código más expresivo y componible.
map(): transformar cada elemento
map(fn) crea un nuevo array aplicando fn a cada elemento del original. El resultado tiene siempre la misma longitud que el array de entrada:
const precios = [10, 25, 50, 100];
// Aplicar descuento del 20%
const conDescuento = precios.map(precio => precio * 0.8);
console.log(conDescuento); // [8, 20, 40, 80]
// Transformar objetos (respuesta de API)
const usuarios = [
{ id: 1, nombre: 'Ana García', activo: true },
{ id: 2, nombre: 'Luis Pérez', activo: false }
];
const nombres = usuarios.map(u => u.nombre);
console.log(nombres); // ['Ana García', 'Luis Pérez']
// map recibe (elemento, índice, array)
const conIndice = ['a', 'b', 'c'].map((letra, i) => `${i}: ${letra}`);
console.log(conIndice); // ['0: a', '1: b', '2: c']
// Error clásico: usar map cuando no necesitas el resultado
// MAL (usa map como forEach):
usuarios.map(u => console.log(u.nombre));
// BIEN: usar forEach cuando no hay transformación
usuarios.forEach(u => console.log(u.nombre));
filter(): seleccionar elementos
filter(fn) devuelve un nuevo array con los elementos para los que fn devuelve un valor truthy. El resultado puede tener menos elementos que el original:
const productos = [
{ nombre: 'Teclado', precio: 89, stock: 15, categoria: 'hardware' },
{ nombre: 'Ratón', precio: 45, stock: 0, categoria: 'hardware' },
{ nombre: 'Monitor', precio: 350, stock: 5, categoria: 'hardware' },
{ nombre: 'Curso JS', precio: 29, stock: 999, categoria: 'digital' }
];
// Productos en stock
const enStock = productos.filter(p => p.stock > 0);
console.log(enStock.length); // 3
// Filtrar por múltiples condiciones
const hardware = productos.filter(p =>
p.categoria === 'hardware' && p.precio < 200 && p.stock > 0
);
console.log(hardware.map(p => p.nombre)); // ['Teclado']
// Eliminar valores falsy de un array
const datos = [0, 1, '', 'hola', null, undefined, false, true, NaN];
const limpios = datos.filter(Boolean);
console.log(limpios); // [1, 'hola', true]
reduce(): acumular en un único valor
reduce(fn, valorInicial) recorre el array y acumula un resultado que puede ser un número, string, objeto o array. Es el más versátil de los tres y el más potente:
const carrito = [
{ nombre: 'Teclado', precio: 89, cantidad: 1 },
{ nombre: 'Ratón', precio: 45, cantidad: 2 },
{ nombre: 'Monitor', precio: 350, cantidad: 1 }
];
// Calcular total
const total = carrito.reduce((suma, item) => {
return suma + item.precio * item.cantidad;
}, 0); // 0 es el valor inicial del acumulador
console.log(total); // 89 + 90 + 350 = 529
// Agrupar por propiedad
const empleados = [
{ nombre: 'Ana', dept: 'tech' },
{ nombre: 'Luis', dept: 'ventas' },
{ nombre: 'Sara', dept: 'tech' },
{ nombre: 'Pedro', dept: 'ventas' }
];
const porDpto = empleados.reduce((grupos, emp) => {
const dept = emp.dept;
grupos[dept] = grupos[dept] || [];
grupos[dept].push(emp.nombre);
return grupos;
}, {});
console.log(porDpto);
// { tech: ['Ana', 'Sara'], ventas: ['Luis', 'Pedro'] }
// Contar ocurrencias
const votos = ['js', 'python', 'js', 'rust', 'js', 'python'];
const conteo = votos.reduce((acc, voto) => {
acc[voto] = (acc[voto] || 0) + 1;
return acc;
}, {});
console.log(conteo); // { js: 3, python: 2, rust: 1 }
Encadenar los tres métodos
La potencia real aparece al encadenarlos: cada método devuelve un array nuevo que el siguiente puede consumir:
const pedidos = [
{ id: 1, cliente: 'Ana', total: 89, estado: 'completado' },
{ id: 2, cliente: 'Luis', total: 450, estado: 'cancelado' },
{ id: 3, cliente: 'Sara', total: 120, estado: 'completado' },
{ id: 4, cliente: 'Pedro', total: 75, estado: 'completado' },
{ id: 5, cliente: 'Marta', total: 320, estado: 'completado' }
];
// Total de pedidos completados con más de 100
const totalCompletadosGrandes = pedidos
.filter(p => p.estado === 'completado') // [1, 3, 4, 5] ? filtrar
.filter(p => p.total > 100) // [3, 5] ? filtrar más
.map(p => p.total) // [120, 320] ? transformar
.reduce((suma, total) => suma + total, 0); // 440 ? reducir
console.log(totalCompletadosGrandes); // 440
// Nombres de clientes con pedidos grandes
const clientesGrandes = pedidos
.filter(p => p.estado === 'completado' && p.total > 100)
.map(p => p.cliente);
console.log(clientesGrandes); // ['Sara', 'Marta']
Cuando veas que necesitas tanto filtrar como transformar sobre los mismos datos, encadenar es más legible que un bucle for con condiciones. Si el rendimiento es crítico con arrays muy grandes, evalúa usar un solo reduce o un bucle imperativo.
