JavaScript-配列を別の配列内に挿入する


87

配列を別の配列内に挿入するより効率的な方法は何ですか。

a1 = [1,2,3,4,5];
a2 = [21,22];

newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];

a2配列が大きい場合、spliceを使用してa2を反復すると、パフォーマンスの観点から少し不自然に見えます。

ありがとう。



2
1つのアイテムではなく配列なので、スプライスは機能しません
ic3

回答:


177

spliceいくつかのapplyトリックと組み合わせて使用できます:

a1 = [1,2,3,4,5];
a2 = [21,22];

a1.splice.apply(a1, [2, 0].concat(a2));

console.log(a1); // [1, 2, 21, 22, 3, 4, 5];

ES2015 +では、spread演算子を代わりに使用して、これを少し良くすることができます

a1.splice(2, 0, ...a2);

10
@icCube:私はベンチマークを実行し、この方法をパトリックの回答の方法と比較しました。例のようにa1とa2で始まり、a2変数をa1に10,000回注入します(その結果、最終的には20,005要素の配列になります)。このメソッドの所要時間:81ms、slice + concatメソッドの所要時間156ms (Chrome 13でテスト済み)。もちろん、これには標準的な注意が伴いますが、ほとんどの場合、読みやすさは速度よりも重要です。
nickf

3
@nick:配列が130000を超えるアイテムapplyを保持している場合、Chromeではstackoverflow例外がスローされます。jsfiddle.net/DcHCYまた、私はFFとIEを信じているjsPerfでも発生します。しきい値は約500kです
いいえ、

スプライスを直接呼び出して引数を渡すのではなく、なぜapplyを使用するのですか?
Peter P.

@FrançoisWahl、これは人々が答えでしばしば無視する深刻な問題です。私はあなたの例を取り上げ、100万個以上の要素で動作するようにしました:stackoverflow.com/a/41466395/1038326
Gabriel Kohen

私はこれを試しました(2017年6月)。小さい配列サイズと大きい配列サイズ(Chrome、FF、およびEdge)の両方の最も速い方法は、target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com / inserting
Alexディマ


14

最初は間違っていた。concat()代わりに使用する必要がありました。

var a1 = [1,2,3,4,5],
    a2 = [21,22],
    startIndex = 0,
    insertionIndex = 2,
    result;    

result = a1.slice(startIndex, insertionIndex).concat(a2).concat(a1.slice(insertionIndex));

例: http : //jsfiddle.net/f3cae/1/

この式は、slice(0, 2)[docs]を使用しての最初の2つの要素を返しますa10は開始インデックスで2、要素はdeleteCountですa1が、変更されていません)。

中間結果[1,2]

次に、concat(a2)[docs]を使用a2しての末尾に追加します[1,2]

中間結果[1,2,21,22]

次に、この式のa1.slice(2)末尾の末尾.concat()で呼び出されます。[1,2,21,22].concat(a1.slice(2))。ます。

slice(2)正の整数引数を持つへの呼び出しは、2番目の要素の後のすべての要素を自然数でカウントして返します(5つの要素があるため、[3,4,5]から返されますa1)。これを別の言い方をすると、特異な整数のインデックス引数はa1.slice()、配列のどの位置から要素を返し始めるかを指示します(インデックス2は3番目の要素です)。

中間結果[1,2,21,22].concat([3,4,5])

最後に、2番目.concat()[3,4,5]の末尾に追加され[1,2,21,22]ます。

結果[1,2,21,22,3,4,5]

変更したくなるかもしれませんがArray.prototype、プロトタイプの継承を使用してArrayオブジェクトを拡張し、上記の新しいオブジェクトをプロジェクトに挿入するだけです。

しかし、端に住んでいる人のために...

例: http : //jsfiddle.net/f3cae/2/

Array.prototype.injectArray = function( idx, arr ) {
    return this.slice( 0, idx ).concat( arr ).concat( this.slice( idx ) );
};

var a1 = [1,2,3,4,5];
var a2 = [21,22];

var result = a1.injectArray( 2, a2 );

7ではなく6アイテムのa1を取得-> [1,2、[21,22]、3,4,5]
ic3

動作します!どうもありがとう、数日待ってみましょうが、これが最良の答えです...奇妙なことに、このためのjs関数がありません。
ic3

私のような、より良い、この1
キムチマン

つまり、JSエンジンは、式が完了する前に4つの配列を返す必要があります。ソリューションのロジックは気に入っていますが、スペースを考慮すると最適ではない場合があります。しかし、私はそれが気の利いた、非常にクロスブラウザー互換性があることに同意します。コレクションで機能するため、spread演算子の使用にはコストがかかりますが、長期的には4つの配列を返すよりも、その方がいいかもしれません。
Anthony Rutledge

3

拡散演算子は、式が、または(配列リテラルの)複数の要素(関数呼び出しのための)複数の引数が期待される場所に拡張することを可能にします。

a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);


3

この質問に対する真に創造的な答えがいくつかあります。これは、配列から始めたばかりの人のための簡単な解決策です。必要に応じて、ECMAScript 3準拠のブラウザーまで機能するように設定できます。

始める前に、スプライスについて知っておいてください。

Mozilla開発者ネットワーク:Array.prototype.splice()

まず、の2つの重要な形式を理解し.splice()ます。

let a1 = [1,2,3,4],
    a2 = [1,2];

方法1)目的のインデックスから始めて、x(deleteCount)要素を削除します。

let startIndex = 0, 
    deleteCount = 2;

a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

方法2)目的の開始インデックスの後の配列の末尾までの要素を削除します。

a1.splice(2); // returns [3,4], a1 would be [1,2]

を使用する.splice()と、目的は分割することですa1上記の2つの形式のいずれかを使用して、ヘッド配列とテール配列する。

メソッド#1を使用すると、戻り値は先頭とa1末尾になります。

let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

今、一挙に頭、体(a2)、尾を連結します

[].concat(head, a2, a1);

したがって、このソリューションは、これまでに紹介した他のソリューションよりも現実の世界に似ています。これはレゴで行うことではありませんか?;-)メソッド#2を使用して実行される関数を次に示します。

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    let tail = target.splice(startIndex); // target is now [1,2] and the head
    return [].concat(target, body, tail);
}

let newArray = insertArray([1, 2, 3, 4], ["a", "b"], 2); // [1, 2, "a", "b", 3, 4]

より短い:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    return [].concat(target, body, target.splice(startIndex));
}

より安全:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*@throws Error The value for startIndex must fall between the first and last index, exclusive.
*/
function insertArray(target, body, startIndex)
{
    const ARRAY_START = 0,
          ARRAY_END = target.length - 1,
          ARRAY_NEG_END = -1,
          START_INDEX_MAGNITUDE = Math.abs(startIndex);

    if (startIndex === ARRAY_START) {
        throw new Error("The value for startIndex cannot be zero (0).");
    }

    if (startIndex === ARRAY_END || startIndex === ARRAY_NEG_END) {
        throw new Error("The startIndex cannot be equal to the last index in target, or -1.");
    }

    if (START_INDEX_MAGNITUDE >= ARRAY_END) {
        throw new Error("The absolute value of startIndex must be less than the last index.");
    }

    return [].concat(target, body, target.splice(startIndex));
}

このソリューションの利点は次のとおりです。

1)空の配列を埋めるという単純な前提がソリューションを支配します。

2)頭、体、尾の命名法は自然に感じられます。

3)への二重呼び出しなし .slice()。スライスは一切ありません。

4)いいえ .apply()。非常に不要です。

5)メソッドの連鎖が回避されます。

6)またはのvar代わりに使用するだけでECMAScript 3および5で動作しますletconstます。

** 7)提示されている他の多くのソリューションとは異なり、体に平手打ちする頭と尾があることを保証します。境界の前または後に配列を追加する場合は、少なくとも以下を使用する必要があります.concat() !!!!

注:スプレッドオペアレーター...を使用すると、これらすべてを簡単に実行できます。


2

splice()繰り返しなしでこれを行う方法を見つけたかった:http : //jsfiddle.net/jfriend00/W9n27/

a1 = [1,2,3,4,5];
a2 = [21,22];

a2.unshift(2, 0);          // put first two params to splice onto front of array
a1.splice.apply(a1, a2);   // pass array as arguments parameter to splice
console.log(a1);           // [1, 2, 21, 22, 3, 4, 5];

汎用関数形式:

function arrayInsertAt(destArray, pos, arrayToInsert) {
    var args = [];
    args.push(pos);                           // where to insert
    args.push(0);                             // nothing to remove
    args = args.concat(arrayToInsert);        // add on array to insert
    destArray.splice.apply(destArray, args);  // splice it in
}

これによりa2配列も変更されますが、これはおそらく非常に望ましくありません。また、あなたがに行く必要はありませんArray.prototypeあなたはすぐそこにスライス機能を持っている場合a1a2
nickf 2011

私はそれがa2を変更していることを知っていました。OPは、それが問題かどうかを判断できます。メソッドを取得するためにプロトタイプに行くことに何か問題はありますか?メソッドが欲しかったので、メソッドにオブジェクトが追加されていたので、より適切に思えましたapply()
jfriend00

まあいや、それはまったく同じ機能ですが、一つは短いです:Array.prototype.splicea1.splice:)
nickf

汎用配列挿入機能を追加しました。
jfriend00

.concat新しい配列を返しますsplice。既存の配列を変更することはありません。する必要がありますargs = args.concat(arrayToInsert);
nickf 2011

0
var a1 = [1,2,3,4,5];
var a2 = [21,22];

function injectAt(d, a1, a2) {
    for(var i=a1.length-1; i>=d; i--) {
        a1[i + a2.length] = a1[i];
    }
    for(var i=0; i<a2.length; i++) {
        a1[i+d] = a2[i];
    }
}

injectAt(2, a1, a2);

alert(a1);

0

ここに特別なトリックのない私のバージョンがあります:

function insert_array(original_array, new_values, insert_index) {
    for (var i=0; i<new_values.length; i++) {
        original_array.splice((insert_index + i), 0, new_values[i]);
    }
    return original_array;
}

この場合、反復ごとにinsert_indexをインクリメントするのではなく、new_values配列を.reverse()するだけの方が簡単な場合があります。もちろん、start_indexが0またはstart_index-1の場合、このソリューションの効率は低くなります。明示的なループなしで.concat()を使用する場合や、unshift()またはpush() `を使用する場合と比較してください.apply()
アンソニー・ラトリッジ

0

あなたは新しいものを作成せずに配列に別の配列を挿入したい場合は、最も簡単な方法は、どちらかを使用することであるpushunshiftapply

例えば:

a1 = [1,2,3,4,5];
a2 = [21,22];

// Insert a1 at beginning of a2
a2.unshift.apply(a2,a1);
// Insert a1 at end of a2
a2.push.apply(a2,a1);

これは、pushunshiftが可変数の引数を取るため、機能します。おまけで、アレイを取り付ける端を簡単に選択できます!


やっただけで、a1.concat(a2)またはa2.concat(a1)私はあなたが提案したものよりも簡単なものを考案しました。ただし、問題は配列の境界の間に配列を挿入することであり、配列を最初または最後に排他的に追加するのではありません。;-)
アンソニー・ラトリッジ

0

別のスレッドで述べたように、上記の答えは非常に大きな配列(200K要素)では機能しません。スプライスと手動プッシュに関するここでの代替回答を参照してください:https : //stackoverflow.com/a/41465578/1038326

Array.prototype.spliceArray = function(index, insertedArray) {
    var postArray = this.splice(index);
    inPlacePush(this, insertedArray);
    inPlacePush(this, postArray);

    function inPlacePush(targetArray, pushedArray) {
  // Not using forEach for browser compatability
        var pushedArrayLength = pushedArray.length;
        for (var index = 0; index < pushedArrayLength; index++) {
           targetArray.push(pushedArray[index]);
       }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.