web-dev-qa-db-ja.com

Javascriptで配列を逆にする最も効率的な方法は何ですか?

私は最近、JavaScriptの配列を反転させる最も効率的な方法は何かと尋ねられました。現時点では、forループを使用して配列をいじることを提案しましたが、ネイティブArray.reverse()メソッドがあることに気付きました。

好奇心のために、これを読むことができるように例を示したり正しい方向を指し示したりして、これを探検するのを手伝ってくれる人はいますか?パフォーマンスの測定方法に関する提案も素晴らしいでしょう。

57
luisgo

この設定に基づいて:

_var array = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
var length = array.length;
_

Array.reverse();は最初または2番目に遅いです!

ベンチマークは次のとおりです。 http://jsperf.com/js-array-reverse-vs-while-loop/5

ブラウザ間で、スワップループは高速です。スワップアルゴリズムには2つの一般的なタイプがあり( Wikipedia を参照)、それぞれに2つのバリエーションがあります。

スワップアルゴリズムには、一時スワップとXORスワップの2種類があります。

2つのバリエーションでは、インデックスの計算方法が異なります。最初のバリエーションでは、現在の左インデックスと右インデックスを比較してから、配列の右インデックスをデクリメントします。 2番目のバリエーションは、現在の左のインデックスと長さを半分で割った値を比較し、各反復で右のインデックスを再計算します。

2つのバリエーションの間に大きな違いがある場合とない場合があります。たとえば、Chrome 18では、一時スワップとXORスワップの最初のバリエーションは2番目のバリエーションよりも60%以上遅くなりますが、Opera 12では、一時的なスワップのバリエーションとXORスワップの両方のパフォーマンスは似ています。

一時的なスワップ:

最初のバリエーション:

_function temporarySwap(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
    {
        var temporary = array[left];
        array[left] = array[right];
        array[right] = temporary;
    }
    return array;
}
_

2番目のバリエーション:

_function temporarySwapHalf(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0; left < length / 2; left += 1)
    {
        right = length - 1 - left;
        var temporary = array[left];
        array[left] = array[right];
        array[right] = temporary;
    }
    return array;
}
_

XORスワップ:

最初のバリエーション:

_function xorSwap(array)
{
    var i = null;
    var r = null;
    var length = array.length;
    for (i = 0, r = length - 1; i < r; i += 1, r -= 1)
    {
        var left = array[i];
        var right = array[r];
        left ^= right;
        right ^= left;
        left ^= right;
        array[i] = left;
        array[r] = right;
    }
    return array;
}
_

2番目のバリエーション:

_function xorSwapHalf(array)
{
    var i = null;
    var r = null;
    var length = array.length;
    for (i = 0; i < length / 2; i += 1)
    {
        r = length - 1 - i;
        var left = array[i];
        var right = array[r];
        left ^= right;
        right ^= left;
        left ^= right;
        array[i] = left;
        array[r] = right;
    }
    return array;
}
_

分解代入と呼ばれる別のスワップ方法があります: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring

割り当ての破壊:

最初のバリエーション:

_function destructuringSwap(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0, right = length - 1; left < right; left += 1, right -= 1)
    {
        [array[left], array[right]] = [array[right], array[left]];
    }
    return array;
}
_

2番目のバリエーション:

_function destructuringSwapHalf(array)
{
    var left = null;
    var right = null;
    var length = array.length;
    for (left = 0; left < length / 2; left += 1)
    {
        right = length - 1 - left;
        [array[left], array[right]] = [array[right], array[left]];
    }
    return array;
}
_

現在、破壊割り当てを使用するアルゴリズムは、それらの中で最も遅いです。 Array.reverse();よりもさらに遅いです。ただし、分解割り当てとArray.reverse();メソッドを使用するアルゴリズムは最も短い例であり、最もきれいに見えます。彼らのパフォーマンスが将来良くなることを願っています。


別の言及は、現代のブラウザーが配列Pushおよびsplice操作のパフォーマンスを改善していることです。

Firefox 10では、配列forおよびPushを使用するこのspliceループアルゴリズムは、一時スワップおよびXORスワップループアルゴリズムに匹敵します。

_for (length -= 2; length > -1; length -= 1)
{
    array.Push(array[length]);
    array.splice(length, 1);
}
_

ただし、他のブラウザーの多くが配列Pushおよびspliceのパフォーマンスに一致するか、それを超えるまで、おそらくスワップループアルゴリズムを使用する必要があります。

72
XP1

ネイティブメソッドは常に高速です。

したがって、可能な場合は_Array.reverse_を使用してください。それ以外の場合、O(1)で実行される実装が最適です;)

それ以外の場合は、このようなものを使用します

_var reverse = function(arr) {
   var result = [],
       ii = arr.length;
   for (var i = ii - 1;i !== 0;i--) {
       result.Push(arr[i]);
   }
   return result;
}
_

ベンチマーク!

1つだけではなくforコンストラクトの3つのステージすべてを使用すると、ループが興味深いことになります。

for(var i = ii - 1; i !== 0;i--)var i = ii - 1;for(;i-- !== 0;)よりも高速です

17
Raynos

簡単な方法で、マップを使用してこれを行うことができます。

let list = [10, 20, 30, 60, 90]
let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...]
13
Srinivas Damam

I Firefoxのバグをオープンしました Firefoxのリバースパフォーマンスが遅いことについて。 Mozillaの誰かが、受け入れられた投稿で使用されているベンチマークを見て、それはかなり誤解を招くと言っています。彼らの分析では、ネイティブメソッドは一般的に配列を反転するのに適しています。 (そうあるべき!)

10
starwed

誰もそれを思い付かなかったので、配列を逆にする方法のリストを完成させるために...

array.sort(function() {
    return 1;
})

While-approachesの2倍の速さですが、それ以外は恐ろしく遅いです。

http://jsperf.com/js-array-reverse-vs-while-loop/5

5
CoDEmanX

スワップ機能は最速です。上記のスワップ関数にわずかに似ていますが、より高速に実行できる、私が書いた逆関数です。

function reverse(array) {
  var first = null;
  var last = null;
  var tmp = null;
  var length = array.length;

  for (first = 0, last = length - 1; first < length / 2; first++, last--) {
    tmp = array[first];
    array[first] = array[last];
    array[last] = tmp;
  }
}

ここでベンチマークを見つけることができます http://jsperf.com/js-array-reverse-vs-while-loop/19

3
Brice Lin

これは、三項演算子を使用して配列を反転させる最も効率的でクリーンな方法です。

function reverse(arr) {
  return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr));
}
console.log(reverse([4, 3, 3, 1]));
3
Arslan shakoor

Javaの例 http://www.leepoint.net/notes-Java/data/arrays/arrays-ex-reverse.html 配列。javascriptへの変換が非常に簡単です。

関数が呼び出される前と関数が呼び出された後の時間を単純にキャプチャするものを使用することをお勧めします。最短の時間/クロックサイクルを要するものが最も速くなります。

2
clamchoda

上記と似ていますが、代わりにスプライスを使用する別の提案:

var myArray=["one","two","three","four","five","six"];
console.log(myArray);
for(i=0;i<myArray.length;i++){
myArray.splice(i,0,myArray.pop(myArray[myArray.length-1]));
}
console.log(myArray);
2
SlavCo Zute

配列の逆バージョンをコピーし、元のままにしたい場合:

a = [0,1,2,3,4,5,6,7,8,9];
b = []
for(i=0;i<a.length;i++){
    b.Push(a.slice(a.length-i-1,a.length-i)[0])
}

Bの出力:

[ 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
1
tarikakyol

配列を永久に変更して要素を反転させる別の例を次に示します。

var theArray = ['a', 'b', 'c', 'd', 'e', 'f'];

function reverseArrayInPlace(array) {
  for (var i = array.length - 1; i >= 0; i -= 1) {
    array.Push(array[i]);
  }
  array.splice(0, array.length / 2);
  return array;
};
reverseArrayInPlace(theArray);
console.log(theArray); // -> ["f", "e", "d", "c", "b", "a"]
1
drjorgepolanco

ここに私が見つけたいくつかのトリックがあります。クレジットの元のソリューションのCodemanxに行きます

array.sort(function() {
   return 1;
})

TypeScriptでは、これを1行に単純化できます

array.sort(() => 1)
var numbers = [1,4,9,13,16];

console.log(numbers.sort(() => 1));

これはJavaScriptの未来になるので、共有したいと思いました。

配列に要素が2つしかない場合の別のトリック

array.Push(array.shift());
1

.slice()。reverse()でこれを行う簡単な方法を見つけました

var yourArray = ["first", "second", "third", "...", "etc"]
var reverseArray = yourArray.slice().reverse()

console.log(reverseArray)

あなたが取得します

["etc", "...", "third", "second", "first"]

ブラウザまたはnode.jsのいずれかのjavascriptランタイムコンソールに以下を貼り付けると、多数の配列に対して直接的なベンチマークテストが実行されます。

私の場合は40,000と言います

var array = Array.from({ length: 40000 }, () =>
  Math.floor(Math.random() * 40000)
);
var beforeStart = Date.now();
var reversedArray = array.map((obj, index, passedArray) => {
  return passedArray[passedArray.length - index - 1];
});
console.log(reversedArray);
var afterCustom = Date.now();
console.log(array.reverse());
var afterNative = Date.now();
console.log(`custom took : ${afterCustom - beforeStart}ms`);
console.log(`native took : ${afterNative - afterCustom}ms`);

スニペットを直接実行するだけで、カスタムバージョンの動作も確認できます。

0

配列の各値を(右から左に)繰り返すreduceRightを使用することもできます。

const myArray = [1, 2, 3, 4, 5]
const reversedArray = myArray.reduceRight((acc, curr) => [...acc, curr], [])
console.log(reversedArray) // [5, 4, 3, 2, 1]
0
Nick D