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()]今月の名前を取得するためだけに使用してみませんか?
ガンボ

1
@Jean:コードが壊れています:あなたがそれを行う方法では、スプライスされた要素を個別にではなく配列としてシフト解除します。apply()実装を機能させるために使用する必要があります
Christoph

今日、ローテーションは月を変更してこのリストを表示します(Dec最初の位置にあります):["Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov"]
Jean Vincent

@Christoph、あなたは正しいです、これは一般的な回転関数としては機能しません。直後に文字列に変換するために使用された場合にのみ機能します。
Jean Vincent

@Jean:あなたは経由でバージョンを修正することができますArray.prototype.unshift.apply(this, this.splice(...))-私のバージョンは同じことを行いますが、用途push()の代わりにunshift()
クリストフ

回答:


62

配列を変更するタイプセーフな汎用バージョン:

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()。これは本当に便利だとは思いませんが(コメントを参照)、簡単な解決策(ただし、多少のハック)は、行を置き換えることです。

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

これで:

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

unshift()代わりに使用するとpush()、Opera 10ではほぼ2倍高速になりますが、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
James

非常に優れた防弾実装ですが、一般的には、不適切な使用法については、例外、または単なる悪い応答に依存することをお勧めします。これは、コードをクリーンで高速に保つためです。正しいパラメータを渡すか、結果を支払うのはユーザーの責任です。良いユーザーに対するペナルティは好きではありません。それ以外は、要求に応じて配列を変更し、前述のように個々の要素の代わりに配列をシフト解除するという私の実装の欠陥に悩まされないため、これは完璧です。これを返すことは、連鎖を可能にするためにも良いです。ほんとありがと。
Jean Vincent

プッシュとスプライスをキャッシュするためだけに、ここでのクロージャーのコストはいくらですか?
Jean Vincent

@Jean:そうですね、クロージャはスコープチェーン全体をキャプチャします。外部関数がトップレベルである限り、影響は無視できるはずであり、とにかくO(1)ですが、Arrayメソッドの検索は呼び出し回数でO(n)です。実装を最適化するとルックアップがインライン化される可能性があるため、利益は得られませんが、長い間対処しなければならなかったかなり愚かなインタープリターでは、より低いスコープで変数をキャッシュすると大きな影響が出る可能性があります
Christoph

1
これは大きな配列を回転させません。今日チェックしたところ、長さが250891アイテムの配列しか処理できませんでした。どうやら、applyメソッドで渡すことができる引数の数は、特定の呼び出しスタックサイズによって制限されています。ES6の用語では、スプレッド演算子も同じスタックサイズの問題に悩まされます。以下に、同じことを行うマップメソッドを示します。速度は遅くなりますが、何百万ものアイテムで機能します。単純なforループまたはwhileループを使用してマップを実装することもできます。これにより、はるかに高速になります。
Redu 2016年

148

あなたは使用することができ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
注意:このメソッドは煩わしいものであり、paramとして送信された配列を操作することに注意してください。送信する前にアレイの複製
fguillen 2016

ここでは、左右の変数とtypescriptですにおけるバージョンですstackblitz.com/edit/typescript-vtcmhp
DživoJelić

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

push、pop、shift、unshift、concat、spliceと同じように、元の配列(this)を変更する必要があります。それ以外は有効です。
Jean Vincent

@Gumbo、なぜwhileループ?nを正にする必要はありません。スプライスは、負の値でも同様に機能します。結局のところ、これは正しいですが、過負荷の警告なしに最初に正しく取得したほとんどのクリストフバージョンです。
Jean Vincent

@Gumbo、私の前のコメントの修正、負の数はsplice(n。this.length)バージョンでのみ機能します。お使いのバージョンのようにsplice(0、n)を使用するには、正のintが必要です。
Jean Vincent

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é

これは確かに素晴らしい答えであり、それは私を助けました。ただし、元の配列を変更しない代替手段を提供するのが最善です。確かに、オリジナルのコピーを作成するのは簡単ですが、メモリを不必要に使用するため、これはベストプラクティスではありません。
ハイブリッドWeb

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

9

これらの回答の多くは、複雑すぎて読みにくいようです。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部分は数えません)。引数に配列とターゲット値を入力するだけです。

これらの関数を組み合わせて、配列が['N'、 'E'、 'S'、 'W']の4人用カードゲームプログラムを作成します。誰かが彼らのニーズのためにコピー/貼り付けしたい場合に備えて、私はそれらを別々に残しました。私の目的では、ゲームのさまざまなフェーズ(ピノクル)でプレイ/アクトの次の順番を探すときに関数を使用します。私は速度のテストを気にしなかったので、誰か他の人が望むなら、遠慮なく結果を知らせてください。

*注意、関数間の唯一の違いは「+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

1
いい答えだ。ダークサイド(PHP)に行く前からでしたか?
ニック

...私はダークサイドで生まれました。
mickmackusa

:)ところで私はここで
ニック

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)];
ニック・F

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 [編集] OK今では明らかではない魔女の方法が最善であるより多くのブラウザがあります

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

私はそれの作品は:)(!それは奇妙なコードです)クロムは、Firefoxのほぼ反対に振る舞うことだけ^^ rotateArray()関数をやっていることを図...ない
molokoloco

このメソッドは非常に興味深いもので、配列内の要素参照を交換することで機能します。配列サイズは決して変更されないため、ループを使用するという欠点があり、非常に高速です。それが示す興味深い点は、Firefoxがループで最速であり、chromeが配列操作で最速であることです。
Jean Vincent

1
コードを分析した後、このソリューションが小さい配列と特定の回転数でのみ高速であることを示す弱点を発見しました:jsperf.com/js-rotate-array/5 すべてのブラウザーの中で、スプライス/シフト解除を備えたGoogleChromeが最速のままです解決。これは、Chromeが他のブラウザよりも優れている配列メソッドの最適化の問題であることを意味します。
Jean Vincent

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

私よりも優れたプログラマーによる少しのリファクタリングのおかげで、最初の試みよりも1〜2行短くなっていますが、効率に関するコメントは大歓迎です。


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

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

1行のコードで:

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

2

受け入れられた回答には、セッションによって異なりますが、100〜300Kアイテム程度のコールスタックサイズよりも大きい配列を処理できないという欠陥があります。たとえば、私が試した現在のChromeセッションでは250891でした。多くの場合、配列が動的にどのサイズに成長するかさえわからない場合があります。ですから、それは深刻な問題です。

この制限を克服するための興味深い方法の1つはArray.prototype.map()、インデックスを循環的に再配置することによって要素を利用およびマッピングすることだと思います。このメソッドは1つの整数引数を取ります。この引数が正の場合はインデックスの増加で回転し、負の場合はインデックスの方向が減少します。これは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));

実際、私は次の2つの問題について批判されました。

  1. DRYの違反が明らかになるため、正または負の入力の条件は必要ありません。すべての負のnには正の等価物があるため、1つのマップでこれを行うことができます(完全に正しい..)
  2. 配列関数は、現在の配列を変更するか、新しい配列を作成する必要があります。シフトが必要かどうかに応じて、関数で実行できます(完全に正しい..)。

次のようにコードを変更することにしました。

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

また; もちろん、のようなJSファンクターArray.prototype.map()は、プレーン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ループを使用して簡単に簡略化でき、速度が大幅に向上します...しかし、実際のところ、受け入れられた答えは、他のいくつかの言語を念頭に置いて開発されたアプローチを持っています。JSには理想的ではありません。配列サイズがコールスタックサイズよりも大きい場合でも実行されません。(私の場合、このセッションでは、250891アイテムのみに制限されていることが判明しました)それで、それがあります。
Redu 2016年

適用されるコールスタックサイズが超える可能性があることについては正しいです。これは速度よりも有効な引数です。インデントを改善してこの点を強調するように回答を編集することを検討してください。
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);
};

ここでパフォーマンスの内訳を確認できます。


残念ながら、それは機能しません。配列をサイズ0に縮小します。これは、特に大きな配列で、最初の実行後に他の正しい実装よりもはるかに高速である理由を説明しています。ベンチマークテストで、テストの順序を入れ替えて、最後に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 = 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 = 4、とを取りますoffset = -4

、、offset = -4およびA = [1,2,3,4,5]が各インデックスに対してoffset + index、大きさ(またはMath.abs(offset))を小さくする場合。

まず、負のnのインデックスの計算について説明しましょう。A[(((n % A.length) + A.length) % A.length)+0]とおびえています。しないでください。Replでそれを解決するのに3分かかりました。

  1. nケースがであるため、ネガティブであることがわかりn < 0ます。数値が配列の範囲よりも大きい場合は、範囲にn % A.lengthマップされます。
  2. n + A.lengthその数をA.lengthに追加して、nを正しい量オフセットします。
  3. nケースがであるため、ネガティブであることがわかりn < 0ます。n + A.lengthその数をA.lengthに追加して、nを正しい量オフセットします。
  4. 次に、モジュロを使用してAの長さの範囲にマップします。計算結果をインデックス可能な範囲にマッピングするには、2番目のモジュールが必要です

    ここに画像の説明を入力してください

  5. 最初のインデックス:-4 + 0 = -4。A.length =5。A.length-4=1。A2は2です。インデックス0を2にマップします。[2,... ]

  6. 次のインデックス、-4 + 1 = -3。5 + -3 = 2 A 2が3 3.マップインデックス1です。[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索引付けは、そうAマッピングする場合、5が範囲外である2の残りの部分で値5 / 50 Aであり、2 Aの値= [0]。[5,1...]
  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
これにより、アレイが回転することはありません。
Jean Vincent

1
True(回答に記載されているとおり)ですが、アレイを回転させる必要がありません。
tgandrews 2010年

1
しかし、これの要点は、タイトルごとに配列を回転させ、特定のオフセットから配列を表示しないことです。同じループを使用して新しい配列を再構築すると、ネイティブの配列メソッドを使用する他のバージョンよりも大幅に遅くなります。さらに、無限ループから抜け出すのを忘れていました。
Jean Vincent

0

配列が大きくなる場合や、頻繁にローテーションする場合は、配列の代わりにリンクリストの使用を検討することをお勧めします。


同意しましたが、質問は配列、より具体的には配列動詞の拡張に関係しています。
Jean Vincent

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)は2つの引数を取ります。配列は回転する必要があり、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. 次に、一時変数を配列の最後の要素に割り当てます。
  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);



0

以下を使用してください-

arr=[1,2,3,4,5]  
let arrs=[]
arrs=arr.slice(d%arr.length).concat(arr.slice(0,d%arr.length))

ここで、d回転数は
最良の解決策であり、O(1)時間計算量を使用したポッピングプッシュのブルートフォース手法を適用する必要はありません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.