Nuevos métodos de Array en JavaScript: toSorted, toReversed, with, findLast, findLastIndex y groupBy

ES2023 y ES2024 llegaron con un conjunto de métodos de array que facilitan dos operaciones habituales: trabajar de forma inmutable sin mutar el original, y buscar o agrupar elementos. Métodos como toSorted, toReversed, with o Object.groupBy se han convertido rápidamente en herramientas cotidianas en código moderno.

toSorted y toReversed: variantes inmutables

El clásico Array.prototype.sort muta el array original, lo que causa bugs cuando el mismo array se reutiliza en varios sitios. toSorted devuelve un nuevo array ordenado sin tocar el original:

const precios = [34.99, 12.50, 89.00, 5.99];

// sort muta el original
const ordenado1 = precios.sort((a, b) => a - b);
console.log(precios === ordenado1); // true — mismo array mutado

// toSorted devuelve uno nuevo
const precios2 = [34.99, 12.50, 89.00, 5.99];
const ordenado2 = precios2.toSorted((a, b) => a - b);
console.log(precios2);   // [34.99, 12.50, 89.00, 5.99] — sin cambios
console.log(ordenado2);  // [5.99, 12.50, 34.99, 89.00]

Lo mismo aplica a toReversed:

const pasos = ['inicio', 'validar', 'guardar', 'notificar'];
const inverso = pasos.toReversed();

console.log(pasos);   // ['inicio', 'validar', 'guardar', 'notificar']
console.log(inverso); // ['notificar', 'guardar', 'validar', 'inicio']

toSpliced: eliminar o insertar sin mutar

toSpliced es la versión inmutable de splice. Devuelve un nuevo array con los cambios aplicados:

const tareas = ['diseño', 'desarrollo', 'testing', 'despliegue'];

// Eliminar "testing" y añadir "QA" y "integración"
const actualizado = tareas.toSpliced(2, 1, 'QA', 'integración');
console.log(tareas);     // ['diseño', 'desarrollo', 'testing', 'despliegue']
console.log(actualizado); // ['diseño', 'desarrollo', 'QA', 'integración', 'despliegue']

// Útil en actualizaciones de estado en React:
// setTareas(prev => prev.toSpliced(indice, 1));

with: cambiar un elemento por índice

with(indice, valor) devuelve una copia del array con un único elemento cambiado. Es la alternativa inmutable a la asignación por índice:

const colores = ['rojo', 'verde', 'azul'];
const actualizado = colores.with(1, 'amarillo');

console.log(colores);    // ['rojo', 'verde', 'azul']
console.log(actualizado); // ['rojo', 'amarillo', 'azul']

// Funciona con índices negativos
const ultimo = colores.with(-1, 'naranja');
console.log(ultimo); // ['rojo', 'verde', 'naranja']

findLast y findLastIndex

Buscan desde el final del array hacia el principio. Útil cuando el elemento que interesa es el más reciente en una lista cronológica:

const eventos = [
  { tipo: 'click', ts: 1000 },
  { tipo: 'scroll', ts: 1200 },
  { tipo: 'click', ts: 1500 },
  { tipo: 'keydown', ts: 1800 },
  { tipo: 'click', ts: 2100 },
];

const ultimoClick = eventos.findLast(e => e.tipo === 'click');
console.log(ultimoClick); // { tipo: 'click', ts: 2100 }

const posicion = eventos.findLastIndex(e => e.tipo === 'click');
console.log(posicion); // 4

at(-1): acceso por índice negativo

El método at(), disponible desde ES2022, permite acceder a elementos por índice negativo de forma nativa sin el hack arr[arr.length - 1]:

const historial = ['pag1', 'pag2', 'pag3', 'pag4'];

console.log(historial.at(-1));  // 'pag4'
console.log(historial.at(-2));  // 'pag3'
console.log(historial.at(0));   // 'pag1'

// También funciona en strings y TypedArrays
const str = 'javascript';
console.log(str.at(-1)); // 't'

Object.groupBy (ES2024)

Object.groupBy agrupa los elementos de un iterable en un objeto según la clave que devuelva la función callback. Es el groupBy que siempre echó en falta en JavaScript y que antes requería Lodash o una implementación manual:

const productos = [
  { nombre: 'Teclado', categoria: 'Periféricos', precio: 45 },
  { nombre: 'Monitor', categoria: 'Pantallas', precio: 299 },
  { nombre: 'Ratón', categoria: 'Periféricos', precio: 29 },
  { nombre: 'Webcam', categoria: 'Periféricos', precio: 79 },
  { nombre: 'Altavoces', categoria: 'Audio', precio: 89 },
];

const porCategoria = Object.groupBy(productos, p => p.categoria);
console.log(porCategoria);
// {
//   Periféricos: [{nombre: 'Teclado',...}, {nombre: 'Ratón',...}, {nombre: 'Webcam',...}],
//   Pantallas:   [{nombre: 'Monitor',...}],
//   Audio:       [{nombre: 'Altavoces',...}]
// }

// Agrupar por rango de precio
const porRango = Object.groupBy(productos, p =>
  p.precio < 50 ? 'económico' : p.precio < 150 ? 'medio' : 'premium'
);

Map.groupBy (ES2024)

Si la clave de agrupación es un objeto (en lugar de un string o número), Map.groupBy evita la conversión a string que haría Object.groupBy:

const ECONOMICO = { label: 'económico', max: 50 };
const MEDIO = { label: 'medio', max: 150 };
const PREMIUM = { label: 'premium', max: Infinity };

const rangos = Map.groupBy(productos, p =>
  p.precio < 50 ? ECONOMICO : p.precio < 150 ? MEDIO : PREMIUM
);

for (const [rango, items] of rangos) {
  console.log(`${rango.label}: ${items.map(p => p.nombre).join(', ')}`);
}

Array.fromAsync (ES2024)

Array.fromAsync convierte un iterable asíncrono en un array, esperando cada elemento antes de continuar:

async function* generarPaginas(url) {
  let pagina = 1;
  while (pagina <= 3) {
    const res = await fetch(`${url}?page=${pagina}`);
    const datos = await res.json();
    yield datos;
    pagina++;
  }
}

const todasLasPaginas = await Array.fromAsync(
  generarPaginas('/api/productos')
);
console.log(todasLasPaginas.length); // 3

Estos métodos no son solo azúcar sintáctico: toSorted, toReversed, toSpliced y with son fundamentales en arquitecturas donde la inmutabilidad es un requisito (Redux, signals, Svelte), y Object.groupBy elimina una de las necesidades más frecuentes de Lodash en proyectos modernos.

COMPARTE ESTE ARTÍCULO

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