web-dev-qa-db-ja.com

オブジェクトの配列をjavascriptの別の配列にコピーする

配列のすべての要素(要素がオブジェクトである場合)を別の配列にコピーして、完全に独立させるにはどうすればよいですか?ある配列の要素を変更して他の配列に影響を与えたくありません。

49
hAlE

宛先配列がまだ存在しない場合...

...slice()またはconcat()を使用できます。 slice()はおそらくより慣用的です(slice(0)も表示されますが、デフォルトは0なので、...):

var destinationArray = sourceArray.slice(); // Probably more idiomatic
// or
var destinationArray = sourceArray.concat();

ES2015(別名ES6)には、Array.fromもあります。これは、任意の配列のようなもの(実際の配列を含む)から新しい配列を作成します。

var destinationArray = Array.from(sourceArray);

Array.fromは、古いJavaScriptエンジン用にシム/ポリフィルできます。)

また、ES2015の時点では、反復可能なスプレッド表記と、配列リテラルを反復可能(配列を含む)で使用できます。

var destinationArray = [...sourceArray];

その後、両方の配列の内容は同じになります。 1つのアレイを変更しても、他のアレイは変更されません。当然、配列エントリがオブジェクトの場合、両方の配列のそのオブジェクトのエントリは同じオブジェクトを指します。これは「深い」コピーではありません。

宛先配列が存在する場合...

...そしてソース配列の内容をそれに追加したい場合は、Pushを使用できます:

destinationArray.Push.apply(destinationArray, sourceArray);

これは、JavaScript関数のPush機能を使用して、宛先配列でapplyを呼び出すことで機能します。これにより、関数呼び出しの引数を配列として指定できます。 Pushは、引数の数だけ要素をプッシュするため、ソース配列から宛先配列に要素をコピーすることになります。

ES2015以降では、反復可能なスプレッド表記(...)を使用して、よりクリーンにすることができます。

destinationArray.Push(...sourceArray);

どちらの場合でも、呼び出しはJavaScriptエンジンの関数引数の最大数によって制限されていることに注意してください(この記事の執筆時点では、すべての主要なエンジンで少なくとも数千になります(数十万ではなく、少なくともChromeのV8 ])。

ES5バージョンは次のとおりです。

var source1, dest1, source2, dest2;

snippet.log("If dest doesn't exist yet:");
source1 = [1, 2, 3, 4];
dest1 = source1.slice(0);
snippet.log("[before change] source1 = " + source1.join(", "));
snippet.log("[before change] dest1 = " + dest1.join(", "));
source1[2] = "three";
dest1[3] = "four";
snippet.log("[after change] source1 = " + source1.join(", "));
snippet.log("[after change] dest1 = " + dest1.join(", "));

snippet.log("If dest already exists and we're just appending:");
source2 = [1, 2, 3, 4];
dest2 = ['a', 'b', 'c', 'd'];
snippet.log("[before append] source2 = " + source2.join(", "));
snippet.log("[before append] dest2 = " + dest2.join(", "));
dest2.Push.apply(dest2, source2);
snippet.log("[before change] source2 = " + source2.join(", "));
snippet.log("[before change] dest2 = " + dest2.join(", "));
source2[2] = "three";
dest2[7] = "four";
snippet.log("[after change] source2 = " + source2.join(", "));
snippet.log("[after change] dest2 = " + dest2.join(", "));
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
94
T.J. Crowder

これを動作させる簡単な方法は以下を使用することです:

var cloneArray = JSON.parse(JSON.stringify(originalArray));

arr.concat()またはarr.splice(0)を取得してディープコピーを作成する際に問題があります。上記のスニペットは完全に機能します。

21
jsbisht

配列を複製するための素晴らしい方法は、配列リテラルスプレッド演算子。これはES2015によって可能になります。

let objArray = [{name:'first'}, {name:'second'}, {name:'third'}, {name:'fourth'}];

let clonedArr = [...objArray];

console.log(clonedArr) // [Object, Object, Object, Object]

このコピーオプションは、MDNのスプレッド演算子https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_operator#Copy_an_arrayのドキュメントにあります。

また、Airbnbのベストプラクティスです。 https://github.com/airbnb/javascript#es6-array-spreads

:通常、ES2015のスプレッド演算子は、配列のコピー中に1レベル深くなります。したがって、多次元配列のコピーには適していません。

4
MauricioLeal
var clonedArray = array.concat();
3
Ilya

2つの重要な注意事項があります。

  1. Angular 1.4.4およびjQuery 3.2.1(これは私の環境です)を使用すると、array.concat()を使用しても機能しません。
  2. array.slice(0)はオブジェクトです。したがって、newArray1 = oldArray.slice(0); newArray2 = oldArray.slice(0)のような操作を行うと、2つの新しい配列は1つの配列のみを参照し、一方を変更すると他方に影響します。

または、newArray1 = JSON.parse(JSON.stringify(old array))を使用すると値のみがコピーされるため、毎回新しい配列が作成されます。

1

参照を保持する場合:

Array.prototype.Push.apply(destinationArray, sourceArray);

1
Veikko Karsikko

NodeJSを使用している場合は、concat()を使用することをお勧めします。他のすべてのケースでは、slice(0)が正常に機能することがわかりました。

0
bean5