JavaScript数组rotate()

我想知道什么是最有效的方法来旋转JavaScript数组。

我想出了这个解决scheme,其中一个正数n向右旋转数组,一个负数n在左边( -length < n < length ):

 Array.prototype.rotate = function( n ) { this.unshift( this.splice( n, this.length ) ) } 

然后可以这样使用:

 var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; months.rotate( new Date().getMonth() ) 

我原来的版本有一个缺陷,正如Christoph在下面的评论中指出的那样,一个正确的版本是(额外的回报允许链接):

 Array.prototype.rotate = function( n ) { this.unshift.apply( this, this.splice( n, this.length ) ) return this; } 

有没有更紧凑和/或更快的解决scheme,可能在JavaScript框架的上下文中? (以下提出的版本都不是更紧凑或更快)

有一个数组旋转内置的JavaScript框架吗? (仍然没有人回答)

types安全的,改变数组的通用版本:

 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; }; })(); 

Jean在评论中提出了代码不支持重载push()splice() 。 我不认为这是真正有用的(见评论),但是一个快速的解决scheme(虽然有些黑客)将取代线

 push.apply(this, splice.call(this, 0, count)); 

与这一个:

 (this.push || push).apply(this, (this.splice || splice).call(this, 0, count)); 

在Opera 10中,使用unshift()而不是push()速度几乎快了一倍,而FF中的差异可以忽略不计; 代码:

 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; }; })(); 

编辑:另请参阅我的新答案与count参数: https : //stackoverflow.com/a/33451102

你可以使用push()pop()shift()unshift()函数:

 function arrayRotate(arr, reverse){ if(reverse) arr.unshift(arr.pop()) else arr.push(arr.shift()) return arr } 

用法:

 arrayRotate(['h','e','l','l','o']); // ['e','l','l','o','h']; arrayRotate(['h','e','l','l','o'], true); // ['o','h','e','l','l']; 

我可能会做这样的事情:

 Array.prototype.rotate = function(n) { return this.slice(n, this.length).concat(this.slice(0, n)); } 

编辑这是一个增变版本:

 Array.prototype.rotate = function(n) { while (this.length && n < 0) n += this.length; this.push.apply(this, this.splice(0, n)); return this; } 

这个函数既可以工作,也可以使用任何数字(即使数字大于数组长度):

 function arrayRotate(arr, count) { count -= arr.length * Math.floor(count / arr.length) arr.push.apply(arr, arr.splice(0, count)) return arr } 

例:

 for(let i = -6 ; i <= 6 ; i++) console.log( arrayRotate( ["H","e","l","l","o"], i).join(''), i ) 

结果:

 "oHell", -6 "Hello", -5 "elloH", -4 "lloHe", -3 "loHel", -2 "oHell", -1 "Hello", 0 "elloH", 1 "lloHe", 2 "loHel", 3 "oHell", 4 "Hello", 5 "elloH", 6 

所以这些答案似乎太复杂,难以阅读。 我不认为我看到有人用concat拼接…

 function rotateCalendar(){ var cal=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"], cal=cal.concat(cal.splice(0,new Date().getMonth())); console.log(cal); // return cal; } 

console.log输出(*在5月份生成):

 ["May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr"] 

至于紧凑性,我可以提供一些通用的单行函数(不包括console.log | return部分)。 只需要在参数中input数组和目标值。

我把这些function组合成一个四人卡牌游戏程序,其中arrays是['N','E','S','W']。 我留下他们分开,以防任何人想要复制/粘贴他们的需求。 为了我的目的,我使用function时,寻求轮到谁在游戏的不同阶段(Pinochle)玩/行动旁边。 我没有为速度testing,所以如果别人想要,随时让我知道结果。

*注意,function之间的唯一区别是“+1”。

 function rotateToFirst(arr,val){ // val is Trump Declarer's seat, first to play arr=arr.concat(arr.splice(0,arr.indexOf(val))); console.log(arr); // return arr; } function rotateToLast(arr,val){ // val is Dealer's seat, last to bid arr=arr.concat(arr.splice(0,arr.indexOf(val)+1)); console.log(arr); // return arr; } 

组合function…

 function rotateArray(arr,val,pos){ // set pos to 0 if moving val to first position, or 1 for last position arr=arr.concat(arr.splice(0,arr.indexOf(val)+pos)); return arr; } var adjustedArray=rotateArray(['N','E','S','W'],'S',1); 

adjustedArray =

 W,N,E,S 

@Christoph,你已经做了一个干净的代码,但比我发现的最慢的60% 。 看看jsPerf上的结果: http ://jsperf.com/js-rotate-array/2 [编辑]现在确定有更多的浏览器一个不明显的女巫方法最好

 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 

当我找不到一个现成的片段来开始一个“今天”的日子列表,我做了这样的(不是一般的,可能远远不如上述的例子精致,但做了这个工作):

 //returns 7 day names with today first function startday() { var days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; var today = new Date(); var start = today.getDay(); //gets day number if (start == 0) { //if Sunday, days are in order return days } else { //if not Sunday, start days with today return days.slice(start).concat(days.slice(0,start)) } } 

感谢一个比我更好的程序员的一些重构,这比我最初的尝试还要短一两行,但是对效率的任何进一步的评论都是值得欢迎的。

请参阅http://jsperf.com/js-rotate-array/8

 function reverse(a, from, to) { --from; while (++from < --to) { var tmp = a[from]; a[from] = a[to]; a[to] = tmp; } } function rotate(a, from, to, k) { var n = to - from; k = (k % n + n) % n; if (k > 0) { reverse(a, from, from + k); reverse(a, from + k, to); reverse(a, from, to); } } 

被接受的答案有一个缺陷,就是不能处理大于依赖会话的调用堆栈大小的数组,而应该在100〜300K左右。 例如,在我尝试的当前Chrome会话中,它是250891.在很多情况下,您甚至可能不知道arrays可能dynamic增长到多大的大小。 所以这是一个严重的问题。

为了克服这个限制,我想一个有趣的方法是利用Array.prototype.map()和通过重新排列循环索引来映射元素。 这个方法有一个整数参数。 如果这个论点是积极的,它将会随着指数的增加而转动,如果减less指数的话则会是负的 这只有O(n)的时间复杂性,并且会返回一个新的数组,而不会在处理数百万个项目时突变所调用的数组。 让我们看看它是如何工作的

 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)); 

这是一个非常简单的方法来移动数组中的项目:

 function rotate(array, stepsToShift) { for (var i = 0; i < stepsToShift; i++) { array.unshift(array.pop()); } return array; } 

这个函数比小数组接受的答案要快一点,但对于大数组来说要快得多。 此function还允许任意数量的旋转数大于数组的长度,这是原始function的限制。

最后,接受的答案如所描述的那样旋转相反的方向。

 const rotateForEach = (a, n) => { const l = a.length; a.slice(0, -n % l).forEach(item => a.push( item )); return a.splice(n % l > 0 ? (-n % l) : l + (-n % l)); } 

和function相当(这似乎也有一些性能优势):

 const rotateReduce = (arr, n) => { const l = arr.length; return arr.slice(0, -n % l).reduce((a,b) => { a.push( b ); return a; }, arr).splice(n % l> 0 ? l + (-n % l) : -n % l); }; 

你可以在这里查看性能细目。

如何增加一个计数器,然后通过数组长度得到除法的余数,以得到你应该在的位置。

 var i = 0; while (true); { var position = i % months.length; alert(months[position]); ++i; } 

除了这个语言的语法应该可以。

如果你的数组将会很大,或者你要旋转很多,你可能要考虑使用链表而不是数组。

@molokoloco我需要一个function,我可以configuration在一个方向旋转 – 真正的前进和错误的后退。 我创build了一个片段,它接受一个方向,一个计数器和一个数组,并输出一个对象,其计数器在适当的方向递增,以及之前,当前和下一个值。 它不会修改原始数组。

我也把它与你的片段同步,尽pipe速度不是很快,但是比你比较的速度要快 – 慢了21% 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) 

我来晚了,但我有一块砖,以增加这些好的答案。 我被要求编写这样一个函数,我首先做了:

 Array.prototype.rotate = function(n) { for (var i = 0; i < n; i++) { this.push(this.shift()); } return this; } 

但是当n很大的时候,它似乎不如下面那样高效:

 Array.prototype.rotate = function(n) { var l = this.length;// Caching array length before map loop. return this.map(function(num, index) { return this[(index + n) % l] }); }