これは、JavaScript 配列の使用に関するヒントについての簡単な記事です。さまざまな方法を使用して 2 つの JS 配列を結合/マージし、各方法の長所/短所について説明します。
まず次の状況を考えてみましょう:
var a = [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ];
var b = [ "foo", "bar", "baz", "bam", "bun", "fun" ];
明らかに、最も単純な組み合わせ結果は次のようになります:
[
1、2、3、4、5、6、7、8、9、
「フー」、「バー」、「バズ」、「バム」、「バン」、「ファン」
]
concat(..)
これは最も一般的なアプローチです:
var c = a.concat( b );
a; // [1,2,3,4,5,6,7,8,9]
b; // ["foo","bar","baz","bam","bun","fun"]
c; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
ご覧のとおり、C はまったく新しい配列で、2 つの配列 a と b の組み合わせを表し、A と B は変更されません。シンプルですよね?
しかし、a に 10,000 個の要素があり、b にも 10,000 個の要素がある場合はどうなるでしょうか? C には 20,000 個の要素があるため、a と b のメモリ使用量は 2 倍になります。
「問題ありません!」とあなたは言います。ガベージ コレクションを実行し、A と B を null に設定すれば、問題は解決しました。
a = b = null // 'a' と 'b' は再利用されます
ははは。要素が数個しかない小さな配列の場合、これは問題ありません。しかし、大規模な配列の場合、またはこのプロセスを頻繁に繰り返す必要があるメモリが限られているシステムの場合、実際には改善の余地がたくさんあります。
ループ挿入
それでは、Array#push(..)
を使用して、ある配列の内容を別の配列にコピーしましょう。
// `b` を `a` に重ねます
for (var i=0; i
a.push( b[i] );
}
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
b = null;
今、配列 a には配列 b の内容が含まれています。
メモリ使用量が向上しているようです。
しかし、配列 a が小さい場合はどうなるでしょうか?メモリと速度の理由から、小さい方の a を b の前に置くこともできます。問題ありません。push(..) を unshift(..) に置き換えるだけです:
// `a` を `b` に変換:
for (var i=a.length-1; i >= 0; i--) {
b.unshift( a[i] );
}
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
機能に関するヒント
しかし、for ループは実に醜く、保守が困難です。もっとうまくできるでしょうか?
これは Array#reduce を使用した最初の試みです:
// `b` を `a` に追加:
a = b.reduce( function(coll,item){
coll.push(アイテム);
リターンコール;
}, a );
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
// または `a` を `b` に変換します:
b = a.reduceRight( function(coll,item){
coll.unshift(item);
リターンコール;
}, b );
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
Array#reduce(..) と Array#reduceRight(..) は便利ですが、少し使いにくいです。 ES6=> のアロー関数はコードの量をいくらか削減しますが、依然として各要素に対して 1 回呼び出す必要がある関数が必要であり、完全ではありません。
これはどうでしょうか:
// `b` を `a` に追加:
a.push.apply( a, b );
a; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
// または `a` を `b` に変換します:
b.unshift.apply( b, a );
b; // [1,2,3,4,5,6,7,8,9,"foo","bar","baz","bam","bun","fun"]
This is a lot better right? Especially since the unshift(..) method doesn't need to worry about the previous reverse sorting here. ES6's span operation will be more beautiful: a.push( ...b) or b.unshift( ...a
Maximum array length limit
The first major issue is that the memory usage has doubled (temporarily of course!) and what is being appended is basically copying elements to the stack via function calls. In addition, different JS engines have limitations on the length of copied data.
So, if the array has a million elements, you will definitely exceed the limit of the call stack allowed for push(...) or unshift(...). Alas, it will do a fine job with a few thousand elements, but you have to be careful not to exceed reasonable length limits.
Note: You can try splice(...), which has the same problem as push(...) and unshift(...).
There is a way to avoid this maximum length limit.
function combineInto(a,b) {
var len = a.length;
for (var i=0; i < len; i=i 5000) {
b.unshift.apply( b, a.slice( i, i 5000 ) );
}
}
Wait a minute, our readability is backwards. That's it, it may get worse as it changes, haha.