JavaScript
Ordenamiento de arrays
Explica cómo ordenar números, textos, fechas y objetos con comparadores consistentes, múltiples criterios, estabilidad, sort y toSorted.
- Última actualización
- Actualizada
- Nivel
- Aplicación
- Fundamentos de programación
JavaScript
Explica cómo ordenar números, textos, fechas y objetos con comparadores consistentes, múltiples criterios, estabilidad, sort y toSorted.
Ordenar significa definir qué elemento debe aparecer antes que otro.
const ordered = numbers.toSorted(
(first, second) => first - second,
);[10, 2, 30].toSorted();
// [10, 2, 30]Sin comparador, los elementos se convierten a strings y se comparan según sus unidades de código UTF-16.
"10" antes de "2"Para números utiliza una función de comparación.
Ascendente:
const ascending = numbers.toSorted(
(first, second) => first - second,
);Descendente:
const descending = numbers.toSorted(
(first, second) => second - first,
);El comparador debe retornar:
0 si first va antes.0 si tienen el mismo orden relativo.0 si second va antes.const values = [3, 1, 2];
values.sort((a, b) => a - b);sort modifica el mismo array.
const values = [3, 1, 2];
const ordered = values.toSorted(
(a, b) => a - b,
);toSorted crea otro array y conserva el original.
Ambos mantienen referencias a los mismos objetos interiores.
products.toSorted(
(first, second) => first.price > second.price,
);Retorna únicamente true o false, convertidos a 1 y 0. Nunca expresa el caso negativo y puede incumplir el contrato.
Usa:
products.toSorted(
(first, second) => first.price - second.price,
);const ordered = products.toSorted(
(first, second) => first.price - second.price,
);Por fecha:
const ordered = events.toSorted(
(first, second) =>
new Date(first.date) - new Date(second.date),
);Crear fechas repetidamente dentro del comparador puede ser costoso. Normaliza antes cuando el volumen lo justifique.
const prepared = events.map((event) => ({
event,
timestamp: Date.parse(event.date),
}));
const ordered = prepared
.toSorted(
(first, second) =>
first.timestamp - second.timestamp,
)
.map(({ event }) => event);const names = ["Álvaro", "Ana", "Édgar"];
const ordered = names.toSorted(
(first, second) =>
first.localeCompare(second, "es"),
);Para ordenar muchas cadenas con las mismas reglas, reutiliza Intl.Collator.
const collator = new Intl.Collator("es", {
sensitivity: "base",
numeric: true,
});
const ordered = names.toSorted(collator.compare);numeric: true permite un orden humano como item2 antes de item10.
const ordered = products.toSorted(
(first, second) => {
const byStatus = first.status.localeCompare(
second.status,
);
if (byStatus !== 0) {
return byStatus;
}
return first.name.localeCompare(second.name);
},
);Una forma compacta para números:
const ordered = items.toSorted(
(first, second) =>
first.priority - second.priority ||
first.createdAt - second.createdAt,
);Úsala solo cuando la precedencia sea fácil de leer.
El ordenamiento de arrays es estable en el estándar moderno: si el comparador devuelve 0, los elementos conservan su orden relativo anterior.
const users = [
{ name: "Ana", group: 1 },
{ name: "Luis", group: 2 },
{ name: "Sara", group: 1 },
];
users.toSorted(
(first, second) => first.group - second.group,
);Dentro del grupo 1, Ana continúa antes de Sara.
Esto permite ordenar primero por un criterio secundario y después por uno principal, aunque un comparador único suele ser más explícito.
Debe ser consistente:
0.a va antes de b, b debe ir después de a.Evita efectos secundarios.
let direction = 1;
values.sort((a, b) => {
direction *= -1;
return (a - b) * direction;
});El resultado no es confiable porque la regla cambia entre llamadas.
const values = [10, NaN, 5];Un comparador como a - b puede retornar NaN, que se trata de forma similar a 0 para el orden. Define una política explícita.
function compareNumbers(first, second) {
const firstInvalid = Number.isNaN(first);
const secondInvalid = Number.isNaN(second);
if (firstInvalid && secondInvalid) return 0;
if (firstInvalid) return 1;
if (secondInvalid) return -1;
return first - second;
}sort y toSorted colocan valores undefined hacia el final sin llamar al comparador para esos pares. Los holes también terminan al final; toSorted produce posiciones con undefined donde el método mutable puede conservar slots vacíos según la operación.
Evita depender de estos detalles: normaliza datos antes de ordenar.
function getProducts() {
return products.sort(byName);
}Cada llamada muta la fuente compartida.
function getProducts() {
return products.toSorted(byName);
}Si el volumen es grande y la fuente no cambia, considera memoizar según identidad o versión de datos.
const collator = new Intl.Collator("es", {
sensitivity: "base",
});
const orderedProducts = products.toSorted(
(first, second) => {
const byAvailability =
Number(second.stock > 0) -
Number(first.stock > 0);
if (byAvailability !== 0) {
return byAvailability;
}
return collator.compare(
first.name,
second.name,
);
},
);Primero aparecen productos disponibles y después se ordenan por nombre.
sort sobre una colección compartida.NaN, null o datos inválidos.toSorted clona los objetos interiores.sort muta; toSorted crea otro array.localeCompare o Intl.Collator sirven para texto humano.¿Por qué [10, 2, 30].sort() no produce el orden numérico esperado?
Porque sin comparador los valores se convierten a strings y se ordenan textualmente. Debe utilizarse (a, b) => a - b para orden ascendente numérico.
Arrays dispersos y casos límite explica las posiciones vacías y por qué distintos métodos pueden tratarlas de manera diferente.