新しい配列を作成せずに、既存のJavaScript配列を別の配列で拡張する方法


973

既存のJavaScript配列を別の配列で拡張する方法、つまりPythonのextendメソッドをエミュレートする方法はないようです。

以下を達成したい:

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

a.concat(b)メソッドがあることは知っていますが、最初の配列を単純に拡張するのではなく、新しい配列を作成します。aが大幅に大きい場合b(つまり、コピーしない場合)に効率的に機能するアルゴリズムが必要aです。

注:これは配列に何かを追加する方法の複製ではありませんか?-ここでの目的は、1つの配列の内容全体を他の配列に追加し、「その場で」、つまり拡張配列のすべての要素をコピーせずに追加することです。


5
その答えに歯ブラシのコメント@から:a.push(...b)。概念的にはトップアンサーと似ていますが、ES6用に更新されています。
tscizzle 2016

回答:


1500

.pushこの方法は、複数の引数を取ることができます。スプレッド演算子を使用して、2番目の配列のすべての要素を引数として渡すことができます.push

>>> a.push(...b)

ブラウザーがECMAScript 6をサポートしていない場合は、.apply代わりに次を使用できます。

>>> a.push.apply(a, b)

あるいは、もっとはっきりしていると思うなら:

>>> Array.prototype.push.apply(a,b)

配列bが長すぎると、これらすべてのソリューションがスタックオーバーフローエラーで失敗することに注意してください(ブラウザーによっては、約100,000要素で問題が発生します)。
それbが十分に短いことを保証できない場合は、他の回答で説明されている標準のループベースの手法を使用する必要があります。


3
これがあなたの最善の策だと思います。それ以外は、反復または適用()の別の行使関与しようとしている
ピーター・ベイリー

44
「b」(拡張する配列)が大きい場合(私のテストではChromeでおよそ150000エントリ)、この回答は失敗します。forループを使用するか、「b」で組み込みの「forEach」関数を使用することをお勧めします。:私の答えを参照してくださいstackoverflow.com/questions/1374126/...
jcdude

35
@Deqing:配列のpushメソッドは任意の数の引数を取ることができ、それらは配列の後ろにプッシュされます。これa.push('x', 'y', 'z')は、a3つの要素で拡張される有効な呼び出しです。apply配列を取り、その要素をすべて関数の位置要素として明示的に与えられているかのように使用する、任意の関数のメソッドです。したがってa.push.apply(a, ['x', 'y', 'z'])、配列を3要素だけ拡張することもできます。さらに、apply最初の引数としてコンテキストを使用します(ここにa再度渡してに追加しますa)。
DzinX 2013

注:このシナリオでは、最初の戻り値は[1,2,3,4,5]ではなく5ですが、その後== [1,2,3,4,5]になります。
jawo 2015

1
さらにもう少し混乱が少ない(それほど長くはない)呼び出しは次のとおり[].push.apply(a, b)です。
niry

259

2018年更新より良い答えは、私の新しいものですa.push(...b)。これは実際には質問に答えたことがないので、もうこれを賛成しないでください。しかし、これは2015年の最初のGoogleへのハッキングでした:)


「JavaScript配列拡張」を単純に検索してここに到達した場合は、を非常にうまく使用できますArray.concat

var a = [1, 2, 3];
a = a.concat([5, 4, 3]);

スレッドスターターが望んでいないため、Concatは新しい配列のコピーを返します。しかし、あなたは気にしないかもしれません(確かにほとんどの種類の用途ではこれで問題ありません)。


また、spread演算子の形式で、これに適したECMAScript 6シュガーがいくつかあります。

const a = [1, 2, 3];
const b = [...a, 5, 4, 3];

(それもコピーします。)


43
質問は明確に述べました:「新しい配列を作成しなくても?」
Wilt

10
@Wilt:これは真実ですが、答えは理由ですが:)これは長い間Googleで最初にヒットしました。また、他のソリューションは醜く、イデオマティックで素晴らしいものを求めていました。最近では、これarr.push(...arr2)はより新しく、より優れており、この特定の質問に対するTechnically Correct(tm)の回答です。
odinho-Velmont

7
聞いたことはありますが、「新しい配列を作成せずにjavascript append array」を検索したところ、60倍の賛成投票の回答が実際の質問に答えないのは残念です;)私は反対投票しませんでした。あなたの答えでそれを明確にしたので。
Wilt

202

ループベースの手法を使用する必要があります。使用に基づくこのページの他の回答は.apply、大規模な配列では失敗する可能性があります。

かなり簡潔なループベースの実装は次のとおりです。

Array.prototype.extend = function (other_array) {
    /* You should include a test to check whether other_array really is an array */
    other_array.forEach(function(v) {this.push(v)}, this);
}

その後、次のことができます。

var a = [1,2,3];
var b = [5,4,3];
a.extend(b);

.apply追加する配列が大きい場合、DzinXの回答(push.applyを使用)および他のベースのメソッドが失敗します(テストでは、Chromeで約150,000エントリ、Firefoxで> 500,000エントリが大きいことがテストで示されています)。このjsperfで発生するこのエラーを確認できます。

大きな配列を2番目の引数として使用して 'Function.prototype.apply'を呼び出すと、呼び出しスタックサイズを超えるため、エラーが発生します。(MDNには、Function.prototype.applyを使用してコールスタックサイズを超過する危険性についての注記があります。「適用および組み込み関数」というタイトルのセクションを参照してください。)

このページの他の回答との速度比較については、このjsperfをチェックしてくださいEaterOfCodeに感謝)。ループベースの実装は、速度はを使用する場合と似Array.push.applyていますが、よりも少し遅くなる傾向がありArray.slice.applyます。

興味深いことに、追加する配列がスパースである場合、forEach上記のベースメソッドはスパース性を利用して、.applyベースメソッドより優れたパフォーマンスを発揮します。これを自分でテストしたい場合は、このjsperfをチェックしてください。

ちなみに、(私がそうであったように)forEach実装をさらに短くして、次のように誘惑しないでください。

Array.prototype.extend = function (array) {
    array.forEach(this.push, this);
}

これはゴミの結果を生み出すからです!どうして?Array.prototype.forEach呼び出す関数に3つの引数を提供するため、これらは次のとおりです(element_value、element_index、source_array)。これらはすべて、forEach「forEach(this.push、this)」を使用する場合、反復ごとに最初の配列にプッシュされます。


1
PS other_arrayが本当に配列であるかどうかをテストするには、オプションのいずれかを選択します。ここで説明:stackoverflow.com/questions/767486/...
jcdude

6
いい答えです。私はこの問題に気づいていませんでした。私はあなたの答えをここに引用しました:stackoverflow.com/a/4156156/96100
Tim Down

2
.push.apply実際に.forEachはv8 よりはるかに高速ですが、最速はまだインラインループです。
Benjamin Gruenbaum 2014年

1
@BenjaminGruenbaum-それを示すいくつかの結果へのリンクを投稿できますか?このコメントの最後にリンクされているjsperfで収集された結果からわかる限り、使用.forEach.push.applyChrome / Chromium(v25以降のすべてのバージョン)よりも高速です。私は単独でv8をテストすることはできませんでしたが、もしあれば、結果をリンクしてください。jsperfを参照してください:jsperf.com/array-extending-push-vs-concat/5
jcdude

1
@jcdude jsperfが無効です。配列内のすべての要素はundefinedなので.forEach、それらをスキップして最速にします。.splice実際には最速ですか?jsperf.com/array-extending-push-vs-concat/18
EaterOfCode

153

私が最近最もエレガントに感じるのは:

arr1.push(...arr2);

スプレッドオペレータのMDNの記事は、 ES2015(ES6)で、この素敵な甘いの方法に言及しています:

より良いプッシュ

例:pushは、配列を既存の配列の最後にプッシュするためによく使用されます。ES5では、これは多くの場合、次のように行われます。

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

スプレッドのあるES6では、これは次のようになります。

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

arr2jcdudeの回答のとおり、コールスタックがオーバーフローするため、それが巨大になることはありません(約100 000アイテム未満に維持してください)。


1
私が間違えたばかりの警告です。ES6バージョンはに非常に近いarr1.push(arr2)です。これは、2つの配列を組み合わせたものではなくarr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2)、配列の配列になるような問題になる可能性がありますarr1 == [['a','b','d']]。簡単な間違いです。このため、以下の2番目の回答をお勧めします。 stackoverflow.com/a/31521404/4808079
Seph Reed

使用時に便利なの.concatは、2番目の引数が配列であってはならないことです。これは、spread演算子とを組み合わせることで実現できますconcat。たとえばarr1.push(...[].concat(arrayOrSingleItem))
Steven Pribilinskiy

これは、ターゲット配列が空でない場合のためのモダンでエレガントなJavaScriptです。
timbo 2017年

これで十分ですか?
Roel

@RoelDelosReyes:はい。
odinho-Velmont 2018年

46

apply()JavaScriptでの最初のいくつかの単語は、それを使用する理由を理解するのに役立ちます。

このapply()メソッドは、this指定された値と、配列として提供された引数を使用して関数を呼び出します。

Pushは、配列に追加する項目のリストを想定していますapply()ただし、このメソッドは、関数呼び出しに必要な引数を配列として受け取ります。これによりpush、組み込みpush()メソッドを使用して、ある配列の要素を別の配列に簡単に入れることができます。

次の配列があるとします。

var a = [1, 2, 3, 4];
var b = [5, 6, 7];

そして単にこれを行います:

Array.prototype.push.apply(a, b);

結果は次のようになります。

a = [1, 2, 3, 4, 5, 6, 7];

ES6では、spread演算子( "... ")。

a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7]; 

現在のところ、すべてのブラウザーでより短くより良いが完全にはサポートされていません。

あなたがしたい場合にも移動し、アレイからすべてba空にする、bプロセスでは、あなたがこれを行うことができます:

while(b.length) {
  a.push(b.shift());
} 

結果は次のようになります。

a = [1, 2, 3, 4, 5, 6, 7];
b = [];

1
またはwhile (b.length) { a.push(b.shift()); }、そうですか?
LarsW

37

jQueryを使用する場合は、$。merge()があります

例:

a = [1, 2];
b = [3, 4, 5];
$.merge(a,b);

結果:a = [1, 2, 3, 4, 5]


1
の実装を(ソースコードで)見つけるにはどうすればよいjQuery.merge()ですか?
ma11hew28

10分ほどかかりました-jQueryはオープンソースであり、ソースはGithubで公開されています。私は「マージ」を検索し、さらにcore.jsファイルのマージ関数の定義と思われるものを見つけました。github.com
午前

19

a.push.apply(a, b)は上記の方法が好きで、必要に応じて次のようなライブラリ関数をいつでも作成できます。

Array.prototype.append = function(array)
{
    this.push.apply(this, array)
}

このように使用します

a = [1,2]
b = [3,4]

a.append(b)

6
push.applyメソッドは、 "配列"引数が大きな配列(Chromeで最大150000エントリなど)の場合にスタックオーバーフローを引き起こす可能性があるため(したがって失敗する可能性があるため)、使用しないでください。「array.forEach」を使用する必要があります-私の回答を参照してください:stackoverflow.com/a/17368101/1280629
jcdude

12

それを使用してそれを行うことが可能ですsplice()

b.unshift(b.length)
b.unshift(a.length)
Array.prototype.splice.apply(a,b) 
b.shift() // Restore b
b.shift() // 

しかし、醜くはありますがpush.apply、少なくともFirefox 3.0では、ほど速くはありません。


9
同じことがわかりました。spliceは
Drew

1
+1これとパフォーマンスの比較を追加してくれてありがとう、この方法をテストする手間が省けた。
jjrv

1
配列bが大きい場合、splice.applyまたはpush.applyを使用すると、スタックオーバーフローが原因で失敗する可能性があります。彼らはまた、より遅い「のための」か「のforEach」ループを使用するよりもされている-このjsPerfを参照してください。jsperf.com/array-extending-push-vs-concat/5と私の答えのstackoverflow.com/questions/1374126/...
jcdude

4

このソリューションは私のために機能します(ECMAScript 6のスプレッドオペレーターを使用)。

let array = ['my', 'solution', 'works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); // Adding to same array
newArray2.push([...array]); // Adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);


1

以下のように、拡張用のポリフィルを作成できます。配列に追加されます。他のメソッドをチェーンできるように、インプレースでそれ自体を返します。

if (Array.prototype.extend === undefined) {
  Array.prototype.extend = function(other) {
    this.push.apply(this, arguments.length > 1 ? arguments : other);
    return this;
  };
}

function print() {
  document.body.innerHTML += [].map.call(arguments, function(item) {
    return typeof item === 'object' ? JSON.stringify(item) : item;
  }).join(' ') + '\n';
}
document.body.innerHTML = '';

var a = [1, 2, 3];
var b = [4, 5, 6];

print('Concat');
print('(1)', a.concat(b));
print('(2)', a.concat(b));
print('(3)', a.concat(4, 5, 6));

print('\nExtend');
print('(1)', a.extend(b));
print('(2)', a.extend(b));
print('(3)', a.extend(4, 5, 6));
body {
  font-family: monospace;
  white-space: pre;
}


0

答えを組み合わせる...

Array.prototype.extend = function(array) {
    if (array.length < 150000) {
        this.push.apply(this, array)
    } else {
        for (var i = 0, len = array.length; i < len; ++i) {
            this.push(array[i]);
        };
    }  
}

9
いいえ、これを行う必要はありません。forEachを使用したforループは、push.applyを使用するよりも高速で、拡張する配列の長さがどのようなものであっても機能します。私の改訂された答えを見てください:stackoverflow.com/a/17368101/1280629 いずれにせよ、150000がすべてのブラウザーに使用するのに適切な数であることをどのようにして知っていますか?これはファッジです。
jcdude

笑。私の答えは認識できませんが、その時に見つかった他のいくつかのコンボ-つまり要約です。心配ありません
zCoder 2013

0

3つ以上のアレイをマージする別のソリューション

var a = [1, 2],
    b = [3, 4, 5],
    c = [6, 7];

// Merge the contents of multiple arrays together into the first array
var mergeArrays = function() {
 var i, len = arguments.length;
 if (len > 1) {
  for (i = 1; i < len; i++) {
    arguments[0].push.apply(arguments[0], arguments[i]);
  }
 }
};

次に、次のように呼び出して印刷します。

mergeArrays(a, b, c);
console.log(a)

出力は次のようになります。 Array [1, 2, 3, 4, 5, 6, 7]


-1

答えは非常に簡単です。

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
(The following code will combine the two arrays.)

a = a.concat(b);

>>> a
[1, 2, 3, 4, 5]

Concatは、JavaScript文字列連結と非常によく似た動作をします。関数を呼び出す配列の最後にあるconcat関数に入力したパラメーターの組み合わせを返します。重要なのは、戻り値を変数に割り当てないと、失われるということです。だから例えば

a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it doesn't do anything with it.

2
MDN のドキュメントArray.prototype.concat()に明記されいるようにこれは新しい配列を返します。OPが明確に要求していたため、既存の配列に追加されません。
Wilt、2016

-2

Array.extend代わりにArray.push、150,000件を超えるレコードに使用します。

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }

    for (let record of arr) {
      this.push(record);
    }

    return this;
  };
}

-6

非常にシンプルで、スプレッドオペレーターを当てにしないか、問題がある場合は適用しません。

b.map(x => a.push(x));

これでいくつかのパフォーマンステストを実行した後、それはひどく遅いですが、新しいアレイを作成しないことに関する質問に答えます。Concatは、jQueryでさえも非常に高速$.merge()です。

https://jsperf.com/merge-arrays19b/1


4
戻り値を使用しない場合.forEachよりも使用する必要.mapがあります。それはあなたのコードの意図をよりよく伝えます。
デヴィッド・

@david-それぞれに。私はのシナックスを好み.mapますが、あなたもすることができますb.forEach(function(x) { a.push(x)} )。実際、それをjsperfテストに追加しました。これは、マップよりも高速です。まだ超遅い。
elPastor

3
これは好みのケースではありません。これらの「機能的」メソッドには、それぞれ固有の意味があります。.mapここに電話するのはとても奇妙に見えます。のようなものb.filter(x => a.push(x))です。それは機能しますが、それがそうでないときに何かが起こっていることを暗示することによって読者を混乱させます。
デヴィッド・

2
@elPastor私のような他の人が座って首のうなじを引っ掻き、あなたが見ることができないあなたのコードで他に何が起こっているのか疑問に思うので、それはあなたの時間の価値があるはずです。ほとんどの通常の場合、重要なのはパフォーマンスではなく意図の明確さです。コードを読んでいる人の時間を尊重してください。あなたがたった1人の場合、コードが多くなる可能性があるためです。ありがとう。
Dmytro Y.

1
@elPastor私は何をするのか正確に知って.map()います、そしてそれが問題です。この知識があれば、結果の配列が必要だと思いますが、それ以外の場合.forEach()は、コールバック関数の副作用について読者に注意を払わせるのに適しています。厳密に言えば、ソリューションは自然数の追加の配列(の結果push)を作成しますが、質問では「新しい配列を作成せずに」と述べています。
Dmytro Y.
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.