私は最近、JavaScriptの配列を反転させる最も効率的な方法は何かと尋ねられました。現時点では、forループを使用して配列をいじることを提案しましたが、ネイティブArray.reverse()
メソッドがあることに気付きました。
好奇心のために、これを読むことができるように例を示したり正しい方向を指し示したりして、これを探検するのを手伝ってくれる人はいますか?パフォーマンスの測定方法に関する提案も素晴らしいでしょう。
この設定に基づいて:
_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
のパフォーマンスに一致するか、それを超えるまで、おそらくスワップループアルゴリズムを使用する必要があります。
ネイティブメソッドは常に高速です。
したがって、可能な場合は_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;)
よりも高速です
簡単な方法で、マップを使用してこれを行うことができます。
let list = [10, 20, 30, 60, 90]
let reversedList = list.map((e, i, a)=> a[(a.length -1) -i]) // [90, 60...]
I Firefoxのバグをオープンしました Firefoxのリバースパフォーマンスが遅いことについて。 Mozillaの誰かが、受け入れられた投稿で使用されているベンチマークを見て、それはかなり誤解を招くと言っています。彼らの分析では、ネイティブメソッドは一般的に配列を反転するのに適しています。 (そうあるべき!)
誰もそれを思い付かなかったので、配列を逆にする方法のリストを完成させるために...
array.sort(function() {
return 1;
})
While-approachesの2倍の速さですが、それ以外は恐ろしく遅いです。
スワップ機能は最速です。上記のスワップ関数にわずかに似ていますが、より高速に実行できる、私が書いた逆関数です。
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
これは、三項演算子を使用して配列を反転させる最も効率的でクリーンな方法です。
function reverse(arr) {
return arr.length < 2 ? arr : [arr.pop()].concat(reverse(arr));
}
console.log(reverse([4, 3, 3, 1]));
Javaの例 http://www.leepoint.net/notes-Java/data/arrays/arrays-ex-reverse.html 配列。javascriptへの変換が非常に簡単です。
関数が呼び出される前と関数が呼び出された後の時間を単純にキャプチャするものを使用することをお勧めします。最短の時間/クロックサイクルを要するものが最も速くなります。
上記と似ていますが、代わりにスプライスを使用する別の提案:
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);
配列の逆バージョンをコピーし、元のままにしたい場合:
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]
配列を永久に変更して要素を反転させる別の例を次に示します。
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"]
ここに私が見つけたいくつかのトリックがあります。クレジットの元のソリューションの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());
.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`);
スニペットを直接実行するだけで、カスタムバージョンの動作も確認できます。
配列の各値を(右から左に)繰り返すreduceRight
を使用することもできます。
const myArray = [1, 2, 3, 4, 5]
const reversedArray = myArray.reduceRight((acc, curr) => [...acc, curr], [])
console.log(reversedArray) // [5, 4, 3, 2, 1]