旋转JavaScript中数组中的元素


86

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

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

Array.prototype.rotateRight = 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() );

正如克里斯托夫在下面的评论中指出的那样,我上面的原始版本有一个缺陷,那就是正确的版本(附加返回值允许链接):

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

是否有可能在JavaScript框架中更紧凑和/或更快速的解决方案?(以下所有建议的版本都不会更紧凑或更快速)

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


1
我不明白您的榜样应该做什么。您为什么不只使用months[new Date().getMonth()]获取当前月份的名称?
Gumbo

1
@Jean:代码已损坏:您这样做的方式是将拼接后的元素作为数组而不是单独移开;您将不得不使用apply()以使您的实施工作有效
Christoph

今天的轮换将修改数月以显示此列表(Dec在第一位置):["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"]
Jean Vincent 2009年

@Christoph,您是对的,它不能用作通用旋转功能。它仅在紧随其后用于转换为字符串时才有效。
让·文森特

@Jean:通过您可以修复您的版本Array.prototype.unshift.apply(this, this.splice(...))-我的版本做同样的事情,但使用push()的替代unshift()
克里斯托夫

Answers:


60

类型安全的通用版本,可更改数组:

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()and的重载的问题splice()。我认为这并不是真的有用(请参阅评论),但是一种快速的解决方案(虽然有点hack)将替换该行

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

2
不错的Array.prototype方法缓存!+1
詹姆斯,

非常好的防弹实现,但总的来说,我会更倾向于依靠异常或仅是错误的响应来解决错误的用法。这样可以保持代码的清洁和快速。用户有责任传递正确的参数或承担后果。我不喜欢对好用户的惩罚。除此之外,这是完美的,因为它确实按要求修改了Array,并且没有我实现中的缺陷,该缺陷使数​​组而不是您提到的单个元素移位。返回此值也更好以允许链接。那谢谢啦。
让·文森特

仅在缓存推入和拼接时,关闭的成本是多少?
让·文森特

@Jean:好吧,闭包捕获了整个作用域链;只要外部函数是顶级函数,其影响就应该可以忽略不计,并且无论如何都是O(1),而查找Array方法的调用次数是O(n)。优化实现可能会内联查找,因此不会有任何收获,但是由于我们不得不处理了很长时间的相当愚蠢的解释器,因此在较低范围内缓存变量可能会产生重大影响
Christoph

1
这不会旋转大数组。我今天检查了一下,它只能处理长度为250891的数组。显然,可以通过apply方法传递的参数数量受到特定调用堆栈大小的限制。用ES6术语来说,扩展运算符也将遭受相同的堆栈大小问题。下面我给出了做同样事情的地图方法。速度较慢,但​​可用于数百万个项目。您也可以通过一个简单的for或while循环来实现map,它将变得更快。
Redu

144

您可以使用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'];

如果您需要count参数,请参阅我的其他答案:https: //stackoverflow.com/a/33451102🖤🧡💚💙💜


它没什么用,尽管根据用途,这并不算什么大问题。
JustGage 2015年

@JustGage,我发布另一个答案与计数支持stackoverflow.com/questions/1985260/...
Yukulélé

2
请注意:请注意此方法是侵入性的,它将操纵作为参数发送的数组。在发送之前
fguillen

这里是一个打字稿版本的左,右变stackblitz.com/edit/typescript-vtcmhp
Dživo耶利奇

38

我可能会做这样的事情:

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

请记住,此功能使原始数组保持不变
Christoph

它需要修改原始数组(this),就像推,弹出,移位,取消移位,连续和拼接一样。除此之外,这是有效的。
让·文森特

@浓汤,为什么while循环?我们不需要使n为正,而拼接也可以为负值。最后,这是正确的,但几乎是Christoph版本,它在没有超负荷警告的情况下首先正确了。
让·文森特

@Gumbo,我之前的评论更正,负数仅适用于splice(n。this.length)版本。在您的版本中使用splice(0,n)需要一个正整数。
让·文森特

1
n = n % this.length在return语句之前添加了处理负数和/或边界数的方法。
iedmrc

25

此函数可以同时使用,并且可以使用任何数字(即使数字大于数组长度):

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(["🧡","💚","💙","💜","🖤"], i), i);
}

结果:

[ "🖤", "🧡", "💚", "💙", "💜" ]    -6
[ "🧡", "💚", "💙", "💜", "🖤" ]    -5
[ "💚", "💙", "💜", "🖤", "🧡" ]    -4
[ "💙", "💜", "🖤", "🧡", "💚" ]    -3
[ "💜", "🖤", "🧡", "💚", "💙" ]    -2
[ "🖤", "🧡", "💚", "💙", "💜" ]    -1
[ "🧡", "💚", "💙", "💜", "🖤" ]    0
[ "💚", "💙", "💜", "🖤", "🧡" ]    1
[ "💙", "💜", "🖤", "🧡", "💚" ]    2
[ "💜", "🖤", "🧡", "💚", "💙" ]    3
[ "🖤", "🧡", "💚", "💙", "💜" ]    4
[ "🧡", "💚", "💙", "💜", "🖤" ]    5
[ "💚", "💙", "💜", "🖤", "🧡" ]    6

使用此功能时,我遇到了一个问题,因为它会更改原始数组。我在这里找到了一些解决方法:stackoverflow.com/questions/14491405
ognockocaten

1
@ognockocaten这不是问题,这是此函数的工作方式。如果你想保持原来的阵列不变,之前克隆它:var arr2 = arrayRotate(arr.slice(0), 5)
Yukulélé

这确实是一个很好的答案,它对我有所帮助。但是,提供一种不会改变原始数组的替代方法是最好的。确保仅创建原始副本很容易,这不是最佳实践,因为它不必要地使用了内存。
混合式网络开发人员,

@Hybridwebdev使用const immutatableArrayRotate = (arr, count) => ArrayRotate(arr.clone(), count)
Yukulélé

7

许多这样的答案似乎过于复杂,难以阅读。我想我没有看到有人在用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 |返回部分)。只需在参数中输入数组和目标值即可。

我将这些功能组合为一个四人纸牌游戏程序,其中的数组为['N','E','S','W']。我将它们分开放置,以防有人想要复制/粘贴它们的需要。出于我的目的,我在寻找游戏的不同阶段(Pinochle)中下一轮的下一个动作/动作时使用这些函数。我没有为速度而烦恼,因此,如果有人愿意,请随时让我知道结果。

*注意,功能之间的唯一区别是“ +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 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

6

使用ES6的价差作为不可变的示例...

[...array.slice(1, array.length), array[0]]

[array[array.items.length -1], ...array.slice(0, array.length -1)]

虽然它可能不是最有效的,但它很简洁。


5

切片和解构的简单解决方案:

const rotate = (arr, count = 1) => {
  return [...arr.slice(count, arr.length), ...arr.slice(0, count)];
};

const arr = [1,2,3,4,5];

console.log(rotate(arr, 1));  // [2, 3, 4, 5, 1]
console.log(rotate(arr, 2));  // [3, 4, 5, 1, 2]
console.log(rotate(arr, -2)); // [4, 5, 1, 2, 3]
console.log(rotate(arr, -1)); // [5, 1, 2, 3, 4]


2
真是太好了!您可以count === 0通过设置默认值来避免检查的需要。这样一来,您就可以做到这一点:const rotate = (arr, n = 1) => [...arr.slice(n, arr.length), ...arr.slice(0, n)];
Nick F

@NickF很好,谢谢!没这样想。
kashesandr

4

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

function rotate(array, stepsToShift) {

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

    return array;
}

3

@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

@molokocolo直观上讲,由于使用循环,您的解决方案运行速度会变慢,因为增量会更高。我已经以5的增量更新了您的测试用例,它的运行速度似乎确实降低了:jsperf.com/js-rotate-array/3
Jean Vincent

我不知道正在做什么的rotateArray()函数^^只是在起作用:)(这是一个奇怪的代码!)Chrome的行为与Firefox几乎相反……
molokoloco 2011年

此方法非常有趣,它通过交换数组中的元素引用来工作。数组大小永远不会改变,这就是为什么它使用循环非常快的缺点。它显示的有趣的是,Firefox是循环最快的,而chrome是数组操作最快的。
让·文森特

1
在分析代码之后,我发现一个弱点,表明该解决方案仅对较小的数组和某些循环计数才更快:jsperf.com/js-rotate-array/5 在所有浏览器中,速度最快的仍然是带有Chromebook的Google Chrome浏览器解。这意味着这实际上是Array方法优化的问题,Chrome的性能要优于其他浏览器。
让·文森特

3

看到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);
  }
}

3

当我找不到现成的代码片段来开始列出“ today”的日子时,我就这样做了(不是很通用,可能不如上面的例子那么精巧,但是可以做到):

//returns 7 day names with today first
function startday() {
    const days = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];
    let today = new Date();
    let 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))
    }
}

由于比我更好的程序员做了一点重构,因此比我最初的尝试要短一两行,但是欢迎您对效率提出更多意见。


3
// 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']

在一行代码中:

let rotated = [...arr.slice((length - rotation) % length, length), ...arr.slice(0, (length - rotation) % length)];

2

可接受的答案存在一个缺陷,即无法处理大于调用堆栈大小的数组,该大小取决于会话,但应大约为100〜300K。例如,在我尝试过的当前Chrome会话中,它是250891。在许多情况下,您甚至可能不知道该数组可以动态增长到多大。所以这是一个严重的问题。

为了克服此限制,我想一种有趣的方法是Array.prototype.map()通过以循环方式重新排列索引来利用和映射元素。此方法采用一个整数参数。如果此参数为正,它将在增加的索引上旋转,而如果为负,则在减少的索引方向上旋转。这仅具有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));

实际上,在以下两个方面我受到批评之后:

  1. 不需要有条件的正输入或负输入,因为它表明违反了DRY。您可以使用一个映射进行此操作,因为每个负n都具有一个正当量(完全正确。)
  2. Array函数应该更改当前数组或创建新数组,您的函数可以根据是否需要进行移位来执行操作(完全正确。)

我决定修改代码如下:

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

再来一次;当然,Array.prototype.map()与纯JS编码的等效函数相比,类似JS的函子慢。为了获得超过100%的性能提升,Array.prototype.rotate()如果我需要旋转生产代码中的数组(例如我尝试使用的数组),则可能是以下选择: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;
};

shift()和splice()尽可能慢,实际上比使用map()或使用函数参数的任何方法要快。从长远来看,更具声明性的shift()和splice()也更易于优化。
Jean Vincent

@Jean Vincent是的,您是对的。.实际上,地图可能会变慢,但我认为理想的旋转逻辑是这样。我只是试图展示逻辑方法。使用for循环可以很容易地将Map简化,并且可以显着提高速度。但是,实际的情况是,公认的答案考虑了其他一些语言的开发方法。这对于JS来说不是很理想。...甚至在数组大小大于调用堆栈大小时也无法运行。(在我的情况下,在本次会议中,结果仅限于250891个项目)。
Redu

您认为适用的调用栈大小可能会超出,这是正确的,而不是速度,这是一个更有效的论点,也可以考虑编辑您的答案以强调这一点并改进缩进。
Jean Vincent

2

此功能比小型阵列的可接受答案要快一点,但对于大型阵列则要快得多。此功能还允许大于数组长度的任意旋转次数,这是原始功能的限制。

最后,接受的答案将按所述相反的方向旋转。

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

和功能等效的功能(似乎也具有一些性能优势):

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

您可以在此处查看性能细分。


不幸的是,它不起作用,它将数组缩小到零大小,这解释了为什么它在首次运行后比任何其他正确实现都要快得多的原因,尤其是在较大的数组上。在基准测试中,如果交换测试顺序,并在最后显示a.length,则会看到问题。
Jean Vincent

2

编辑:: 嘿,结果是迭代太多了。没有循环,没有分支。

对于任何大小的n,仍可使用负n表示向右旋转,正n表示向左旋转,无突变

function rotate(A,n,l=A.length) {
  const offset = (((n % l) + l) %l)
  return A.slice(offset).concat(A.slice(0,offset))
}

这是傻笑的代码高尔夫版本

const r = (A,n,l=A.length,i=((n%l)+l)%l)=>A.slice(i).concat(A.slice(0,i))

EDIT1 :: *无分支 ,无变异的实现。

所以,嘿,原来我有一个我不需要的分支。这是一个可行的解决方案。负数num =向右旋转| num | 正数=左旋转数

function r(A,n,l=A.length) {
  return A.map((x,i,a) => A[(((n+i)%l) + l) % l])
}

该方程((n%l) + l) % l精确地映射了任意任意大的n值的正数和负数

原版的

左右旋转。以正向左n旋转,以负向右旋转n

适用于巨大的输入n

无突变模式。这些答案中的变异太多。

而且,操作比大多数答案要少。没有弹出,没有推动,没有接头,没有转移。

const rotate = (A, num ) => {
   return A.map((x,i,a) => {
      const n = num + i
      return n < 0 
        ? A[(((n % A.length) + A.length) % A.length)]
        : n < A.length 
        ? A[n] 
        : A[n % A.length]
   })
}

要么

 const rotate = (A, num) => A.map((x,i,a, n = num + i) => 
  n < 0
    ? A[(((n % A.length) + A.length) % A.length)]
    : n < A.length 
    ? A[n] 
    : A[n % A.length])

//test
rotate([...Array(5000).keys()],4101)   //left rotation
rotate([...Array(5000).keys()],-4101000)  //right rotation, num is negative

// will print the first index of the array having been rotated by -i
// demonstrating that the rotation works as intended
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,-i)[0])
}) 
// prints even numbers twice by rotating the array by i * 2 and getting the first value
//demonstrates the propper mapping of positive number rotation when out of range
[...Array(5000).keys()].forEach((x,i,a) => {
   console.log(rotate(a,i*2)[0])
})

说明:

将A的每个索引映射到索引偏移处的值。在这种情况下

offset = num

如果offset < 0然后offset + index + positive length of A将指向相反的抵消。

如果offset > 0 and offset < length of A然后简单地将当前索引映射到A的偏移索引。

否则,对偏移量和长度取模,以将偏移量映射到数组的边界中。

就拿offset = 4offset = -4

offset = -4和时A = [1,2,3,4,5],对于每个索引,offset + index将使幅度(或Math.abs(offset))变小。

让我们首先解释负n的索引的计算。A[(((n % A.length) + A.length) % A.length)+0]被吓倒了 不用了 我花了3分钟在一个Repl上解决它。

  1. 我们知道n是负面的,因为情况是这样n < 0。如果该数字大于Array的范围,n % A.length则将其映射到该范围。
  2. n + A.length将该数字加到A.length以抵消n的正确数量。
  3. 我们知道n是负面的,因为情况是这样n < 0n + A.length将该数字加到A.length以抵消n的正确数量。
  4. Next使用模将其映射到A的长度范围。第二模数是将计算结果映射到可索引范围所必需的

    在此处输入图片说明

  5. 第一个索引:-4 + 0 = -4。则为a.length = 5则为a.length - 4 = 1,一种2是2地图索引0至2。[2,... ]

  6. 下一个索引-4 + 1 = -3。5 + -3 = 2,一种2是3.地图索引1至3。[2,3... ]
  7. 等等。

相同的过程适用于offset = 4。当offset = -4和时A = [1,2,3,4,5],对于每个索引,offset + index将使幅度更大。

  1. 4 + 0 = 0。将A [0]映射到A [4]的值。[5...]
  2. 4 + 1 = 5,5在索引时超出范围,因此将A 2映射到其余值5 / 50。A 2 = A [0]处的值。[5,1...]
  3. 重复。

2
function rotate(arr, k) {
for (var i = 0; i < k+1; i++) {
    arr.push(arr.shift());
}
return arr;
}
//k work as an index array
console.log(rotate([1, 2, 7, 4, 5, 6, 7], 3)); //[5,6,7,1,2,7,4]
console.log(rotate([-1, -100, 3, 99], 2));     //[99,-1,-100,3]

1
Follow a simpler approach of running a loop to n numbers and shifting places upto that element.

function arrayRotateOne(arr, n) {
  for (let i = 0; i < n; i++) {
    arr.unshift(arr.pop());
  }
  return arr;
}
console.log( arrayRotateOne([1,2,3,4,5,6],2));



function arrayRotateOne(arr,n) {
  for(let i=0; i<n;i++){
      arr.push(arr.shift());
      console.log('execute',arr)
    }
     return arr;
 }

console.log(arrayRotateOne([1,2,3,4,5,6],2));


1

非变异解决方案

var arr = ['a','b','c','d']
arr.slice(1,arr.length).concat(arr.slice(0,1)

有突变

var arr = ['a','b','c','d']
arr = arr.concat(arr.splice(0,1))

1

我正在分享我在旋转木马上旋转所使用的解决方案。当数组大小小于时,它可能会中断displayCount,但是您可以添加额外的条件以在数组大小较小时停止旋转,或者也可以将主数组并置* displayCount次。

function rotate(arr, moveCount, displayCount) {
  const size = arr.length;

  // making sure startIndex is between `-size` and `size`
  let startIndex = moveCount % size;
  if (startIndex < 0) startIndex += size; 

  return [...arr, ...arr].slice(startIndex, startIndex + displayCount);
}

// move 3 to the right and display 4 items
// rotate([1,2,3,4,5], 3, 4) -> [4,5,1,2]

// move 3 to the left and display 4 items
// rotate([1,2,3,4,5], -3, 4) -> [3,4,5,1]

// move 11 to the right and display 4
// rotate([1,2,3,4,5], 3, 4) -> [2,3,4,5]

0

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

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

除了语言语法,这应该可以正常工作。


2
这不会以任何方式旋转阵列。
让·文森特

1
正确(如答案中所述),但无需旋转数组。
tgandrews

1
但是,这样做的全部目的是按照标题旋转一个数组,而不是显示某个偏移量的数组。如果要使用相同的循环来重建新数组,它将比使用本机Array方法的其他版本慢得多。另外,您忘记了跳出无限循环。
让·文森特

0

如果数组很大并且/或者旋转很多,您可能需要考虑使用链表而不是数组。


同意,但问题与数组有关,更具体地说,涉及数组动词。
让·文森特

0

@molokoloco我需要一个可以配置为沿一个方向旋转的函数-true表示前进,false表示后退。我创建了一个代码片段,该代码片段包含一个方向,一个计数器和一个数组,并输出一个对象,该对象的计数器在适当的方向以及先前值,当前值和下一个值递增。它不会修改原始数组。

我还针对您的代码片段进行了计时,尽管它没有更快,但比您比较的要快,但速度却慢了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)

这不会旋转阵列。要回答这个问题,您需要返回一个数组,该数组的第一个项目位于计数器的位置。
Jean Vincent

0

我来晚了,但我要补充一些好的答案。我被要求编写这样一个函数的代码,我首先这样做:

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

0

我不确定这是否是最有效的方法,但是我喜欢它的读取方式,因为我已经在生产中对其进行了测试,它对于大多数大型任务来说足够快。

function shiftRight(array) {
  return array.map((_element, index) => {
    if (index === 0) {
      return array[array.length - 1]
    } else return array[index - 1]
  })
}

function test() {
  var input = [{
    name: ''
  }, 10, 'left-side'];
  var expected = ['left-side', {
    name: ''
  }, 10]
  var actual = shiftRight(input)

  console.log(expected)
  console.log(actual)

}

test()


0

本机的,快速的,小巧的,语义化的,可在旧引擎上运行且“易变”的引擎。

function rotateArray(offset, array) {
    offset = -(offset % array.length) | 0 // ensure int
    return array.slice(offset).concat(
        array.slice(0, offset)
    )
}

0

**使用最新版本的JS,我们可以轻松构建它**

 Array.prototype.rotateLeft = function (n) {
   this.unshift(...this.splice(-(n), n));
    return this
  }

这里移动:转数,可以传递随机数的数组

let a = [1, 2, 3, 4, 5, 6, 7];
let moves = 4;
let output = a.rotateLeft(moves);
console.log("Result:", output)

0

ArrayJS中的以下内置方法可以很容易地旋转数组,显然这些方法本质上是不可变的。

  • push:将项目插入数组的末尾。
  • pop:从数组末尾删除项目。
  • unshift:将项目插入数组的开头。
  • shift:从数组的开头删除项目。

下面的解决方案(ES6)带有两个参数,需要旋转数组,而n,应旋转数组的次数。

const rotateArray = (arr, n) => {
  while(arr.length && n--) {
    arr.unshift(arr.pop());
  }
  return arr;
}

rotateArray(['stack', 'overflow', 'is', 'Awesome'], 2) 
// ["is", "Awesome", "stack", "overflow"]

可以将其添加到Array.prototype,并可以在整个应用程序中使用

Array.prototype.rotate = function(n) {
 while(this.length && n--) {
   this.unshift(this.pop());
 }
 return this;
}
[1,2,3,4].rotate(3); //[2, 3, 4, 1]

0

使用for循环。步骤如下

  1. 将数组的第一个元素存储为临时变量。
  2. 然后从左向右交换。
  3. 然后将temp变量分配给数组的最后一个元素。
  4. 重复这些步骤以获得转数。

function rotateLeft(arr, rotations) {
    let len = arr.length;
    for(let i=0; i<rotations; i++){ 
        let temp = arr[0];
        for(let i=0; i< len; i++){
            arr[i]=arr[i+1];
        }
        arr[len-1]=temp;
    }
    return arr;
}

let arr = [1,2,3,4,5];

let rotations = 3;
let output = rotateLeft(arr, rotations);
console.log("Result Array => ", output);



-2

不确定效率,但是我会以这种非变异的方式来实现:

	Array.prototype.rotate = function( n ) {
  
		 return this.map( (item, index)=> this[ (this.length + index + n)%this.length ] )
	}


因此,答案“不确定效率”的第一部分就是说“我不知道这是否完全可以回答您的问题”。最初的问题不是问“旋转数组的好方法是什么”,而是专门询问紧凑性和速度(分别是内存和时间效率)。问题只与效率有关,您还没有回答它的那一部分。
richardpringle
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.