En Javascript, es muy habitual que tengamos una estructura de datos
Método | Descripción |
---|---|
.reverse() ⚠️ | Invierte el orden de elementos del array. |
.toReversed() ✅ | Devuelve una copia del array, con el orden de los elementos invertido. |
.sort() ⚠️ | Ordena los elementos del array bajo un criterio de ordenación alfabética. |
.sort(criterio) ⚠️ | Idem, pero bajo un criterio de ordenación indicado por criterio . |
.toSorted() ✅ | Devuelve una copia del array, con los elementos ordenados. |
.toSorted(criterio) ✅ | Idem, pero ordenado por el criterio establecido por parámetro. |
- ✅ El array original está seguro (no muta).
- ⚠️ El array original cambia (muta).
Antes de continuar, establezcamos un punto de partida, donde tenemos un array de letras con cierto orden específico, en nuestro caso orden alfabético:
const elements = ["A", "B", "C", "D", "E", "F"];
Ahora, vamos a modificar dicho array mediante los métodos mencionados.
El método .toReversed()
y .reverse()
En primer lugar, el método reverse()
reordena los elementos del array en orden inverso, es decir, si tenemos [5, 4, 3]
lo modifica de modo que obtenemos [3, 4, 5]
. Veamos su funcionamiento con el ejemplo anterior, y una serie de matices que conviene aclarar:
const elements = ["A", "B", "C", "D", "E", "F"];
const reversedElements = elements.reverse();
reversedElements // ["F", "E", "D", "C", "B", "A"]
elements // ["F", "E", "D", "C", "B", "A"]
reversedElements === elements // true
Observa que aquí hay algo que nos puede llamar poderosamente la atención. Aparentemente, hemos creado un nuevo array reversedElements
al cuál le asignamos el resultado de ejecutar el método .reverse()
sobre el array elements
.
En un primer vistazo, esto podría darnos a entender que se ha creado un nuevo array con el orden inverso, pero comprobemos que tanto el nuevo array reversedElements
como el original elements
han sido modificados. Además, en la última linea podemos ver que se trata de la misma estructura de datos. Lo que realmente ha ocurrido es que el array original elements
ha mutado, y el nuevo array resultante no es más que una referencia al original.
Si queremos crear un nuevo array independiente del original, tendríamos que hacer lo siguiente:
const elements = ["A", "B", "C", "D", "E", "F"];
const reversedElements = structuredClone(elements).reverse();
reversedElements // ["F", "E", "D", "C", "B", "A"]
elements // ['A', 'B', 'C', 'D', 'E', 'F']
reversedElements === elements // false
Utilizando structuredClone()
, lo que hacemos es crear una copia de la estructura original elements
, y luego invertirla. Ahora si, tenemos dos arrays independientes.
Otra solución, mucho más sencilla es utilizar el nuevo método .toReversed()
.reverse()
, pero sin mutar el original:
const elements = ["A", "B", "C", "D", "E", "F"];
const reversedElements = elements.toReversed();
reversedElements // ["F", "E", "D", "C", "B", "A"]
elements // ["A", "B", "C", "D", "E", "F"]
reversedElements === elements // false
Tienes más información detallada en este artículo sobre las estrategias de clonación en Javascript:
Object.assign()
,spread
,structuredClone
y otras: clonar objetos en Javascript.
El método toSorted()
y .sort()
Cualquier estructura de datos sort()
. Este método realiza una ordenación de los elementos del array, con la peculiaridad que siempre realizará una ordenación alfabética:
const names = ["Alberto", "Zoe", "Ana", "Mauricio", "Bernardo"];
const sortedNames = names.sort();
sortedNames // ["Alberto", "Ana", "Bernardo", "Mauricio", "Zoe"]
names // ["Alberto", "Ana", "Bernardo", "Mauricio", "Zoe"]
sortedNames === names // true
Efectivamente, vemos que ahora el array sortedNames
está ordenado alfabéticamente. Sin embargo, ocurre exactamente lo mismo que en el caso anterior. Nuevamente, tenemos que utilizar la función structuredClone()
para hacer una copia y que sea independiente:
const names = ["Alberto", "Zoe", "Ana", "Mauricio", "Bernardo"];
const sortedNames = structuredClone(names).sort();
sortedNames // ["Alberto", "Ana", "Bernardo", "Mauricio", "Zoe"]
names // ["Alberto", "Zoe", "Ana", "Mauricio", "Bernardo"];
sortedNames === names // false
Nuevamente, otra solución mucho más sencilla podría ser utilizar la función .toSorted()
.sort()
, pero manteniendo intacto el array original, sin mutarlo:
const names = ["Alberto", "Zoe", "Ana", "Mauricio", "Bernardo"];
const sortedNames = names.toSorted();
sortedNames // ["Alberto", "Ana", "Bernardo", "Mauricio", "Zoe"]
names // ["Alberto", "Zoe", "Ana", "Mauricio", "Bernardo"]
sortedNames === names // false
Sin embargo, la ordenación anterior se realizó sobre un array de elementos .sort()
como el método .toSorted()
:
const numbers = [1, 8, 2, 32, 9, 7, 4];
const sortedNumbers = numbers.toSorted();
sortedNumbers // [1, 2, 32, 4, 7, 8, 9]
numbers // [1, 8, 2, 32, 9, 7, 4]
Esto ocurre porque, al igual que en el ejemplo anterior, el tipo de ordenación que se realiza por defecto es una ordenación alfabética, mientras que en esta ocasión buscamos una ordenación natural, que es la que se suele utilizar con números (no nos interesa que el 32 se encuentre antes del 9).
Esto se puede hacer en Javascript, pero requiere pasarle por parámetro al sort()
lo que se llama una función de comparación o función de ordenación.
Función de comparación
Una función de ordenación o una función de comparación, lo que hace es establecer un criterio de ordenación diferente al que actúa por defecto, personalizado a través de una función que se le pasa por parámetro al método sort()
o toSorted()
:
const numbers = [1, 8, 2, 32, 9, 7, 4];
const alphabeticOrder = (a, b) => a + b;
const naturalOrder = (a, b) => a - b;
const alphaNumbers = numbers.toSorted(alphabeticOrder); // [1, 8, 2, 32, 9, 7, 4]
const naturalNumbers = numbers.toSorted(naturalOrder); // [1, 2, 4, 7, 8, 9, 32]
Como se puede ver en este fragmento de código, hemos creado dos funciones, en formato arrow function para que sean más compactas: alphabeticOrder
y naturalOrder
. Cada una de ellas tiene un criterio de ordenación diferente. Observa que se las pasamos por parámetro al método toSorted()
para indicarle como debe hacer la ordenación.
En el caso de alphabeticOrder
, esa es la función de ordenación que tiene por defecto el método .sort()
o .toSorted()
, es decir, es lo mismo que no pasarle ningún parámetro. Sin embargo, en el caso de naturalOrder
si que estamos alterando el criterio de ordenación, y le indicamos como hacerlo para ordenar números bajo un criterio de ordenación natural.
Algoritmo de ordenación
Si eres una persona de naturaleza curiosa, es posible que te preguntes que hace exactamente esa función de comparación, que a priori, parece que sólo realiza una operación. Primero, analicemos los pasos hechos hasta ahora:
const elements = [8, 4];
const sortedElements = elements.toSorted(); // [4, 8];
// Recordemos que esto es equivalente a lo siguiente:
const sortedElements = elements.toSorted(function(a, b) { return a + b });
// Si extraemos la función de ordenación:
const alphaOrder = function(a, b) { return a + b };
const sortedElements = elements.toSorted(alphaOrder);
// Si la cambiamos a función flecha
const alphaOrder = (a, b) => a + b;
const sortedElements = elements.toSorted(alphaOrder);
Ahora, si profundizamos en la tarea que realiza el sort()
o toSorted()
, lo que hace concretamente es analizar pares de elementos del array en cuestión para ordenarlos. El primer elemento es a
y el segundo elemento es b
. Entonces, se ejecutará la función, la cuál devolverá un resultado:
const elements = [8, 4];
const alphaOrder = (a, b) => {
console.log(`a(${a}) + b(${b}) = ${a+b}`);
return a + b;
}
const sortedElements = elements.toSorted(alphaOrder);
Observa que la primera vez que ejecutas el toSorted()
, la función de comparación tomará como b
el número 8
y como a
el número 4
. Entonces, realizará la operación 4 + 8
, que dará como resultado 12
. Dependiendo de ese resultado, se realizará una acción:
- Si la función
(a,b)
devuelve un número mayor que cero (>0
), se fija el ordenb, a
(b primero). - Si la función
(a,b)
devuelve un número igual a cero (0
), se mantiene igual. - Si la función
(a,b)
devuelve un número menor que cero (<0
), se fija el ordena, b
(a primero).
Quizás se entienda mejor cambiando un poco el código:
const elements = [8, 4];
const alphaOrder = (a, b) => {
if (a > b) return 1;
if (a < b) return -1;
return 0;
}
const sortedElements = elements.toSorted(alphaOrder);
Sin embargo, poner la operación a + b
es mucho más compacto, puesto que no nos interesa el valor devuelvo concretamente, sino sólo si es mayor, menor o igual a cero.
Esta operación se va realizando para todos los pares de elementos del array y se repite hasta que están todos ordenados según el criterio de ordenación utilizado. Esto es lo que se conoce como el método de la burbuja, uno de los sistemas de ordenación más sencillos y prácticos.