Métodos de array en JavaScript I: map, filter y reduce

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.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP