Rotar los elementos de una matriz en JavaScript
Me preguntaba cuál era la forma más eficaz de rotar una matriz de JavaScript.
Se me ocurrió esta solución, donde un positivo n
rota la matriz hacia la derecha y un negativo n
hacia la izquierda ( -length < n < length
):
Array.prototype.rotateRight = function( n ) {
this.unshift( this.splice( n, this.length ) );
}
Que luego se puede utilizar de esta manera:
var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
months.rotate( new Date().getMonth() );
Mi versión original anterior tiene un defecto, como lo señala Christoph en los comentarios a continuación, una versión correcta es (el retorno adicional permite encadenar):
Array.prototype.rotateRight = function( n ) {
this.unshift.apply( this, this.splice( n, this.length ) );
return this;
}
¿Existe una solución más compacta y/o más rápida, posiblemente en el contexto de un marco de JavaScript? (ninguna de las versiones propuestas a continuación es más compacta o más rápida)
¿Existe algún marco de JavaScript con una rotación de matriz incorporada? (Aún no ha respondido nadie)
Puede utilizar push()
, pop()
y shift()
métodos unshift()
:
function arrayRotate(arr, reverse) {
if (reverse) arr.unshift(arr.pop());
else arr.push(arr.shift());
return arr;
}
uso:
arrayRotate([1, 2, 3, 4, 5]); // [2, 3, 4, 5, 1];
arrayRotate([1, 2, 3, 4, 5], true); // [5, 1, 2, 3, 4];
Si necesita count
un argumento, vea mi otra respuesta:
https://stackoverflow.com/a/33451102 🖤🧡💚💙💜
Versión genérica con seguridad de tipos que muta la matriz:
Array.prototype.rotate = (function() {
// save references to array functions to make lookup faster
var push = Array.prototype.push,
splice = Array.prototype.splice;
return function(count) {
var len = this.length >>> 0, // convert to uint
count = count >> 0; // convert to int
// convert count to value in range [0, len)
count = ((count % len) + len) % len;
// use splice.call() instead of this.splice() to make function generic
push.apply(this, splice.call(this, 0, count));
return this;
};
})();
En los comentarios, Jean planteó el problema de que el código no admite la sobrecarga de push()
y splice()
. No creo que esto sea realmente útil (ver comentarios), pero una solución rápida (aunque algo así como un truco) sería reemplazar la línea
push.apply(this, splice.call(this, 0, count));
Con este:
(this.push || push).apply(this, (this.splice || splice).call(this, 0, count));
Usar unshift()
en lugar de push()
es casi el doble de rápido en Opera 10, mientras que las diferencias en FF fueron insignificantes; el código:
Array.prototype.rotate = (function() {
var unshift = Array.prototype.unshift,
splice = Array.prototype.splice;
return function(count) {
var len = this.length >>> 0,
count = count >> 0;
unshift.apply(this, splice.call(this, count % len, len));
return this;
};
})();
Probablemente haría algo como esto:
Array.prototype.rotate = function(n) {
n = n % this.length;
return this.slice(n, this.length).concat(this.slice(0, n));
}
Editar Aquí hay una versión mutadora:
Array.prototype.rotate = function(n) {
n = n % this.length;
while (this.length && n < 0) n += this.length;
this.push.apply(this, this.splice(0, n));
return this;
}
Esta función funciona en ambas direcciones y con cualquier número. (incluso con un número mayor que la longitud de la matriz):
function arrayRotate(arr, count) {
const len = arr.length
arr.push(...arr.splice(0, (-count % len + len) % len))
return arr
}
uso:
for(let i = -6 ; i <= 6 ; i++) {
console.log(arrayRotate(['🧡', '💚', '💙', '💜', '🖤'], i), i)
}
resultado:
[ '💚', '💙', '💜', '🖤', '🧡' ] -6
[ '🧡', '💚', '💙', '💜', '🖤' ] -5
[ '🖤', '🧡', '💚', '💙', '💜' ] -4
[ '💜', '🖤', '🧡', '💚', '💙' ] -3
[ '💙', '💜', '🖤', '🧡', '💚' ] -2
[ '💚', '💙', '💜', '🖤', '🧡' ] -1
[ '🧡', '💚', '💙', '💜', '🖤' ] 0
[ '🖤', '🧡', '💚', '💙', '💜' ] 1
[ '💜', '🖤', '🧡', '💚', '💙' ] 2
[ '💙', '💜', '🖤', '🧡', '💚' ] 3
[ '💚', '💙', '💜', '🖤', '🧡' ] 4
[ '🧡', '💚', '💙', '💜', '🖤' ] 5
[ '🖤', '🧡', '💚', '💙', '💜' ] 6