I was wondering what was the most efficient way to rotate a JavaScript array.
I came up with this solution, where a positive n
rotates the array to the
The accepted answer has a flaw of not being able to handle arrays larger than the call stack size which depends on the session but should be around like 100~300K items. For instance, in the current Chrome session that i tried it was 250891. In many cases you may not even know to what size the array might dynamically grow into. So that's a serious problem.
To overcome this limitation, I guess one interesting method is utilizing Array.prototype.map()
and mapping the elements by rearranging the indices in a circular fashion. This method takes one integer argument. If this argument is positive it will rotate on increasing indices and if negative on decreasing indices direction. This has only O(n) time complexity and will return a new array without mutating the one it's called upon while handling millions of items without any problem. Let see how it works;
Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this
: n > 0 ? this.map((e,i,a) => a[(i + n) % len])
: this.map((e,i,a) => a[(len - (len - i - n) % len) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
b = a.rotate(2);
console.log(JSON.stringify(b));
b = a.rotate(-1);
console.log(JSON.stringify(b));
Actually after i have been criticized on two matters as follows;
I have decided to modify the code as follows;
Array.prototype.rotate = function(n) {
var len = this.length;
return !(n % len) ? this.slice()
: this.map((e,i,a) => a[(i + (len + n % len)) % len]);
};
var a = [1,2,3,4,5,6,7,8,9],
b = a.rotate(10);
console.log(JSON.stringify(b));
b = a.rotate(-10);
console.log(JSON.stringify(b));
Then again; of course the JS functors like Array.prototype.map()
are slow compared to their equivalents coded in plain JS. In order to gain more than 100% performance boost the following would probably be my choice of Array.prototype.rotate()
if i ever need to rotate an array in production code like the one i used in my attempt on String.prototype.diff()
Array.prototype.rotate = function(n){
var len = this.length,
res = new Array(this.length);
if (n % len === 0) return this.slice();
else for (var i = 0; i < len; i++) res[i] = this[(i + (len + n % len)) % len];
return res;
};
Native, fast, small, semantic, works on old engines and "curryable".
function rotateArray(offset, array) {
offset = -(offset % array.length) | 0 // ensure int
return array.slice(offset).concat(
array.slice(0, offset)
)
}
@Christoph, you've done a clean code, but 60% slowest than this one i found. Look at the result on jsPerf : http://jsperf.com/js-rotate-array/2 [Edit] OK now there is more browsers an that not obvious witch methods the best
var rotateArray = function(a, inc) {
for (var l = a.length, inc = (Math.abs(inc) >= l && (inc %= l), inc < 0 && (inc += l), inc), i, x; inc; inc = (Math.ceil(l / inc) - 1) * inc - l + (l = inc))
for (i = l; i > inc; x = a[--i], a[i] = a[i - inc], a[i - inc] = x);
return a;
};
var array = ['a','b','c','d','e','f','g','h','i'];
console.log(array);
console.log(rotateArray(array.slice(), -1)); // Clone array with slice() to keep original
// Example of array to rotate
let arr = ['E', 'l', 'e', 'p', 'h', 'a', 'n', 't'];
// Getting array length
let length = arr.length;
// rotation < 0 (move left), rotation > 0 (move right)
let rotation = 5;
// Slicing array in two parts
let first = arr.slice( (length - rotation) % length, length); //['p', 'h', 'a' ,'n', 't']
let second = arr.slice(0, (length - rotation) % length); //['E', 'l', 'e']
// Rotated element
let rotated = [...first, ...second]; // ['p', 'h', 'a' ,'n', 't', 'E', 'l', 'e']
In one line of code:
let rotated = [...arr.slice((length - rotation) % length, length), ...arr.slice(0, (length - rotation) % length)];
@molokoloco I needed a function that I could configure to rotate in a direction - true for forward and false for backward. I created a snippet that takes a direction, a counter and an array and outputs an object with the counter incremented in the appropriate direction as well as prior, current, and next values. It does NOT modify the original array.
I also clocked it against your snippet and although it is not faster, it is faster than the ones you compare yours with - 21% slower http://jsperf.com/js-rotate-array/7 .
function directionalRotate(direction, counter, arr) {
counter = direction ? (counter < arr.length - 1 ? counter + 1 : 0) : (counter > 0 ? counter - 1 : arr.length - 1)
var currentItem = arr[counter]
var priorItem = arr[counter - 1] ? arr[counter - 1] : arr[arr.length - 1]
var nextItem = arr[counter + 1] ? arr[counter + 1] : arr[0]
return {
"counter": counter,
"current": currentItem,
"prior": priorItem,
"next": nextItem
}
}
var direction = true // forward
var counter = 0
var arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'];
directionalRotate(direction, counter, arr)
with es6 syntax
function rotLeft(a, d) {
const removed = a.splice(0,d);
return [...a, ...removed];
}