配列を値でコピー


1745

JavaScriptの配列を別の配列にコピーする場合:

var arr1 = ['a','b','c'];
var arr2 = arr1;
arr2.push('d');  //Now, arr1 = ['a','b','c','d']

新しい独立した配列ではなくarr2、と同じ配列を参照していることに気付きましたarr1。配列をコピーして2つの独立した配列を取得するにはどうすればよいですか?


3
現在、Chrome 53とFirefox 48ではslicesplice操作と新しいスプレッドオペレーターのパフォーマンスが優れており、Array.from実装がはるかに遅いようです。見perfjs.fnfo
Pencroff

jsben.ch/#/wQ9RU <=このベンチマークは、アレイをコピーするさまざまな方法の概要を提供します
EscapeNetscape


この配列(プリミティブの文字列が含まれている1)のためには使用することができvar arr2 = arr1.splice();、あなたの配列の要素はリテラルの構造(すなわち含まれている場合、ディープコピーに、この技術は動作しません[]{})またはプロトタイプオブジェクト(すなわちfunction () {}newなど)。さらなる解決策については、以下の私の答えを参照してください。
tfmontague 2017

16
あなたが特徴ES6を使用して検討するかもしれないので、それは、2017年だ:let arr2 = [...arr1]; developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
ハインリック

回答:


2701

これを使って:

var newArray = oldArray.slice();

基本的に、slice()操作は配列を複製し、新しい配列への参照を返します。

また、次の点にも注意してください。

参照、文字列、および数値(実際のオブジェクトではない)の場合、slice()オブジェクト参照を新しい配列にコピーします。元の配列と新しい配列の両方が同じオブジェクトを参照しています。参照オブジェクトが変更された場合、変更は新しい配列と元の配列の両方に表示されます。

文字列や数値などのプリミティブは不変なので、文字列や数値の変更は不可能です。


9
パフォーマンスに関して、以下のjsPerfテストは、var arr2 = arr1.slice()がvar arr2 = arr1.concat();と同じくらい速いことを実際に示しています。JSPerf:jsperf.com/copy-array-slice-vs-concat/5およびjsperf.com/copy-simple-arrayjsperf.com/array-copy/5の結果は、テストコードが有効かどうか疑問に思っている点に私を驚かせました。
コーエン

94
これはすでに大量の賛成票を受け取っていますが、残念ながらJSでの参照を適切に記述しているため、別の価値があります。
ウェイン、

34
@GáborIm単に読みやすくするためにライブラリ全体を追加しますか?本当に?読みやすさが気になる場合は、コメントを追加します。参照:var newArray = oldArray.slice(); // oldArrayをnewArrayに複製
dudewad

12
@GáborImreわかります。しかし、私の意見にライブラリ全体を含めることによって特定のエンジニアリングの問題に答えることは役に立たない、それは設計膨張です。私は開発者がそれをたくさんしていると思います、そしてあなたは単一の関数を書く必要性を置き換えるためにフレームワーク全体を含んだプロジェクトに行き着きます。でも私のMOだけです。
dudewad 2016

5
教訓:空の配列を提供する.slice()と混同しないでください.splice()。大きな違い。
クレイジーピーター

531

JavaScriptでは、ディープコピーテクニックは配列の要素に依存します。そこから始めましょう。

3種類の要素

要素は、リテラル値、リテラル構造、またはプロトタイプです。

// Literal values (type1)
const booleanLiteral = true;
const numberLiteral = 1;
const stringLiteral = 'true';

// Literal structures (type2)
const arrayLiteral = [];
const objectLiteral = {};

// Prototypes (type3)
const booleanPrototype = new Bool(true);
const numberPrototype = new Number(1);
const stringPrototype = new String('true');
const arrayPrototype = new Array();
const objectPrototype = new Object(); // or `new function () {}`

これらの要素から、3種類の配列を作成できます。

// 1) Array of literal-values (boolean, number, string) 
const type1 = [true, 1, "true"];

// 2) Array of literal-structures (array, object)
const type2 = [[], {}];

// 3) Array of prototype-objects (function)
const type3 = [function () {}, function () {}];

ディープコピーテクニックは3つのアレイタイプに依存します

配列の要素のタイプに基づいて、さまざまな手法を使用してディープコピーを作成できます。

要素タイプごとのJavascriptディープコピーテクニック

  • リテラル値(TYPE1)のアレイ、、、及び技術は、リテラル値(ブール値、数値、文字列)のみを有するディープコピーアレイに使用することができます。ここで、Spreadオペレーターは最高のパフォーマンスを発揮します(https://measurethat.net/Benchmarks/Show/4281/0/spread-array-performance-vs-slice-splice-concat)。
    [...myArray]myArray.splice(0)myArray.slice()myArray.concat()[...myArray]

  • リテラル値(type1)およびリテラル構造(type2)
    配列このJSON.parse(JSON.stringify(myArray))手法は、リテラル値(ブール値、数値、文字列)およびリテラル構造(配列、オブジェクト)のディープコピーに使用できますが、プロトタイプオブジェクトには使用できません。

  • すべての配列(type1、type2、type3)
    jQuery $.extend(myArray)手法を使用して、すべての配列型をディープコピーできます。UnderscoreLo-dashなどのライブラリは、jQuery と同様のディープコピー機能を提供しますが$.extend()、パフォーマンスは低下します。さらに驚くべきことに、http://jsperf.com/js-deep-copy/15$.extend()JSON.parse(JSON.stringify(myArray))手法よりもパフォーマンスが優れています。 また、サードパーティのライブラリ(jQueryなど)を避けている開発者には、次のカスタム関数を使用できます。$ .extendよりもパフォーマンスが高く、すべての配列をディープコピーします。

function copy(aObject) {
  if (!aObject) {
    return aObject;
  }

  let v;
  let bObject = Array.isArray(aObject) ? [] : {};
  for (const k in aObject) {
    v = aObject[k];
    bObject[k] = (typeof v === "object") ? copy(v) : v;
  }

  return bObject;
}

質問に答えるために...

質問

var arr1 = ['a','b','c'];
var arr2 = arr1;

arr2は、arr1と同じ配列を指し、新しい独立した配列ではないことに気付きました。配列をコピーして2つの独立した配列を取得するにはどうすればよいですか?

回答

arr1はリテラル値(ブール値、数値、または文字列)の配列なので、spread演算子...が最高のパフォーマンスを発揮する、上記で説明した任意のディープコピー手法を使用できます。

// Highest performance for deep copying literal values
arr2 = [...arr1];

// Any of these techniques will deep copy literal values as well,
//   but with lower performance.
arr2 = arr1.slice();
arr2 = arr1.splice(0);
arr2 = arr1.concat();
arr2 = JSON.parse(JSON.stringify(arr1));
arr2 = $.extend(true, [], arr1); // jQuery.js needed
arr2 = _.extend(arr1); // Underscore.js needed
arr2 = _.cloneDeep(arr1); // Lo-dash.js needed
arr2 = copy(arr1); // Custom-function needed - as provided above

1
これらのアプローチの多くはうまく機能しません。代入演算子を使用すると、の元のリテラル値を再割り当てする必要がありarr1ます。それがそうなることは非常にまれです。spliceobliterates を使用するarr1ので、それはまったくコピーではありません。JSON配列内のいずれかの値が関数であるか、プロトタイプ(などDate)がある場合、使用は失敗します。
Dancrumb、2014

スプライスの使用は部分的な解決策です。JSONよりもはるかに多くのケースで失敗します。Spliceは、値を移動するときに、文字列と数値のディープコピーを作成します。コピーを返すとは言われていません。
tfmontague 2014年

1
なぜsplice(0)?それはslice()であるべきではありませんか?私はそれがスプライスが行う元の配列を変更しないことになっていると思います。@JamesMontagne
15年

2
spliceは、元の配列(浅いコピー)の要素へのポインターを作成します。splice(0)は、数値または文字列である配列内の要素に新しいメモリ(ディープコピー)を割り当て、他のすべての要素タイプ(浅いコピー)にポインターを作成します。開始値0をスプライス関数メソッドに渡すことにより、元の配列の要素はスプライスされないため、変更されません。
tfmontague

1
実際、配列のタイプは「何か」の配列だけです。[0,"1",{2:3},function random() {return 4;}, [[5,6,7],[8,9,10],[11,12,13]]]と他の配列の間に違いはありません。
wizzwizz4

189

配列スプレッド...を使用して配列をコピーできます。

const itemsCopy = [...items];

また、既存の配列がその一部である新しい配列を作成したい場合:

var parts = ['shoulders', 'knees'];
var lyrics = ['head', ...parts, 'and', 'toes'];

配列の広がりは、すべての主要なブラウザーでサポートされていますが、古いサポートが必要な場合は、typescriptまたはbabelを使用して、ES5にコンパイルしてください。

スプレッドの詳細


1
これはディープコピーでは機能しません。JavaScriptで配列をディープクローンします
SNag

151

jQueryは必要ありません... 実例

var arr2 = arr1.slice()

これは、開始位置から配列をコピーします 0の最後ます。

プリミティブ型(文字列、数値など)で期待どおりに機能することに注意し、参照型の予期される動作を説明することも重要です...

参照タイプの配列がある場合は、タイプのように言いObjectます。配列コピーされますが、両方の配列には同じへの参照が含まれますObject。したがって、この場合、配列は実際にコピーされますが、参照によって配列がコピーされているように見えます。


12
いいえ、これは深いコピーにはなりません。
jondavidjohn 2014年

これを試して; var arr2 = JSON.stringify(arr1); arr2 = JSON.parse(arr2);
pradeep1991singh

2
この回答と承認された回答の違いは何ですか?
Isaac Pak

指定した例のコンソールでエラーが発生する "TypeError:window.addEvent is not a function"
Ravi Sharma

72

の代替sliceconcat、2つの方法で使用できます。意図された動作が非常に明確であるため、これらの最初のものはおそらくもっと読みやすいです:

var array2 = [].concat(array1);

2番目の方法は次のとおりです。

var array2 = array1.concat();

コーエンは(コメントで)この後者の方法の方がパフォーマンスが優れていると指摘しました

これが機能する方法は、 concat方法はメソッドが、それが呼び出されたオブジェクトの要素と、それに引数として渡された配列の要素で構成される新しい配列を作成することです。したがって、引数が渡されない場合は、単に配列をコピーします。

Lee Penkmanもコメントの中で、可能性array1undefinedある場合は、次のように空の配列を返すことができると指摘しています。

var array2 = [].concat(array1 || []);

または、2番目の方法の場合:

var array2 = (array1 || []).concat();

あなたもでこれを行うことができますことに注意してくださいslicevar array2 = (array1 || []).slice();


31
実際には、次のことも実行できます。var array2 = array1.concat(); パフォーマンスに関してははるかに高速です。(JSPerf:jsperf.com/copy-simple-arrayおよびjsperf.com/copy-array-slice-vs-concat/5
Cohen

5
array1が配列でない場合、たとえば、未定義の場合はを[].concat(array1)返すことに注意して[array1]ください[undefined]。私は時々そうしますvar array2 = [].concat(array1 || []);
リー・ペンクマン14

60

これは私が多くのアプローチを試した後のやり方です:

var newArray = JSON.parse(JSON.stringify(orgArray));

これにより、最初のコピーとは関係のない(浅いコピーではない)新しい深いコピーが作成されます。

また、これは明らかにイベントと関数のクローンを作成しませんが、1行で実行できるので、あらゆる種類のオブジェクト(配列、文字列、数値、オブジェクトなど)に使用できます。


4
これは最高です。私はずっと前に同じ方法を使用しており、古い学校の再帰ループにはもはや意味がないと思います
Vladimir Kharlampidi

1
このオプションは、グラフのような構造をうまく処理しないことに注意してください。循環が存在するとクラッシュし、共有参照が保持されません。
ルーベン

1
これDateは、プロトタイプを持つものなど、実際には失敗します。さらに、undefinedsはsに変換されnullます。
Dancrumb、2014

7
CPUとメモリの両方でテキストにシリアル化してからオブジェクトに構文解析することの全体的な非効率性についてコメントする勇気のある人はいませんか?
ローレンスドル2014

3
このソリューションは、機能した唯一のものです。slice()の使用は、実際には偽のソリューションです。

20

上記のメソッドの一部は、数値や文字列などの単純なデータ型を処理するときにうまく機能しますが、配列に他のオブジェクトが含まれていると、これらのメソッドは失敗します。ある配列から別の配列にオブジェクトを渡そうとすると、オブジェクトではなく参照として渡されます。

JavaScriptファイルに次のコードを追加します。

Object.prototype.clone = function() {
    var newObj = (this instanceof Array) ? [] : {};
    for (i in this) {
        if (i == 'clone') 
            continue;
        if (this[i] && typeof this[i] == "object") {
            newObj[i] = this[i].clone();
        } 
        else 
            newObj[i] = this[i]
    } return newObj;
};

そして単に使用する

var arr1 = ['val_1','val_2','val_3'];
var arr2 = arr1.clone()

それが動作します。


2
このコードをページに追加すると、このエラーが表示されます 'Uncaught RangeError:Maximum call stack size超過'
sawe

1
申し訳ありませんが、arr1が宣言されていない場合、このエラーはChromeで発生します。上記のコードをコピーして貼り付けたところ、エラーが発生しましたが、配列arr1を宣言すると、エラーは発生しません。arr2のすぐ上にarr1を宣言することで、答えを改善できます。arr1を宣言しなければならなかったことを認識していなかった「私たち」がかなりいます(私があなたの答えを評価しているとき、私は急いでいたためです)そして、「ちょうどうまくいく」何かが必要でした)
sawe

.slice()配列にオブジェクトが含まれていても問題なく機能します:jsfiddle.net/edelman/k525g
Jason

7
@Jasonですが、オブジェクトはまだ同じオブジェクトを指しているため、一方を変更すると他方も変更されます。jsfiddle.net/k525g/1
サミュエル

優れたコード。私が持っている1つの質問、私は実際に次のように1つの配列を別の配列にコピーしようとしましたvar arr1 = new Array()そしてvar arr2 = arr1; arr2で何かを変更すると、arr1にも変更が発生します。ただし、作成したクローンプロトタイプを使用すると、実際にはその配列の完全に新しいインスタンスが作成されます。つまり、コピーされます。これはブラウザの問題ですか?またはデフォルトでjavascriptは、誰かがvar arr2 = arr1を実行したときにポインタを使用して一方を他方を指すように2つの変数を設定し、整数変数で発生しないのはなぜですか?jsfiddle.net/themhz/HbhtAを
2013


17

個人的には、Array.fromの方が読みやすいソリューションだと思います。ところで、ブラウザのサポートに注意してください。

//clone
let x = [1,2,3];
let y = Array.from(x);

//deep clone
let clone = arr => Array.from(arr,item => Array.isArray(item) ? clone(item) : item);
let x = [1,[],[[]]];
let y = clone(x);

1
はい、これは非常に読みやすいです。.slice()解決策は完全に直感的です。これをありがとう。
Banago

15

重要!

ここでの答えのほとんどは特定のケースで機能しますます。

深いオブジェクトやネストされたオブジェクトや小道具を気にしない場合(ES6)を使用します。

let clonedArray = [...array]

ただし、ディープクローンを作成する場合は、代わりにこれを使用します。

let cloneArray = JSON.parse(JSON.stringify(array))


lodashユーザーの場合:

let clonedArray = _.clone(array) ドキュメンテーション

そして

let clonedArray = _.cloneDeep(array) ドキュメンテーション



9

array.slice();のソリューションに追加する 多次元配列がある場合、サブ配列は参照によってコピーされることに注意してください。あなたができることは、各サブ配列を個別にループしてslice()することです

var arr = [[1,1,1],[2,2,2],[3,3,3]];
var arr2 = arr.slice();

arr2[0][1] = 55;
console.log(arr2[0][1]);
console.log(arr[0][1]);

function arrCpy(arrSrc, arrDis){
 for(elm in arrSrc){
  arrDis.push(arrSrc[elm].slice());
}
}

var arr3=[];
arrCpy(arr,arr3);

arr3[1][1] = 77;

console.log(arr3[1][1]);
console.log(arr[1][1]);

同じことがオブジェクトの配列にも当てはまり、参照によってコピーされます。手動でコピーする必要があります


この回答は、ページの上部に近い場所に値します!私は多次元サブ配列を使用していて、内部配列が常にvalではなくrefによってコピーされていた理由を理解できませんでした。この単純なロジックは私の問題を解決しました。できれば+100を差し上げます。
Mac

8

プリミティブ値は常にその値によって渡されます(コピーされます)。ただし、複合値は参照によって渡されます。

では、このarrをどのようにコピーするのでしょうか?

let arr = [1,2,3,4,5];

ES6でアレイをコピーする

let arrCopy = [...arr]; 

ES5でn配列をコピー

let arrCopy = arr.slice(); 
let arrCopy = [].concat(arr);

「let arrCopy = arr」が値で渡されないのはなぜですか?

Object / ArrayなどのCompound値で1つの変数を別の変数に渡すと、動作が異なります。参照値をオブジェクトに渡すcopand値に署名演算子を使用します。これが、arr要素を削除または追加すると、両方の配列の値が変化する理由です。

例外:

arrCopy[1] = 'adding new value this way will unreference';

変数に新しい値を割り当てると、参照自体が変更され、元のオブジェクト/配列には影響しません。

続きを読む


6

Javascriptの配列オブジェクトで知っているようには参照によるが、後で元の配列を変更せずに配列をコピーするにはどうすればよいですか?

いくつかの方法があります:

あなたのコードにこの配列があると想像してください:

var arr = [1, 2, 3, 4, 5];

1)次のように、関数内の配列をループして新しい配列を返します。

 function newArr(arr) {
      var i=0, res = [];
      while(i<arr.length){
       res.push(arr[i]);
        i++;
       }
   return res;
 }

2)スライス方式を使用して、スライスは配列の一部をスライスするためのもので、元の配列に触れることなく配列の一部をスライスします。スライスでは、配列の開始と終了を指定しない場合は全体をスライスします配列し、基本的に配列の完全なコピーを作成するので、簡単に言うことができます:

var arr2 = arr.slice(); // make a copy of the original array

3)また、連絡方法、これは2つの配列をマージするためのものですが、配列の1つを指定するだけで、基本的には新しい連絡先配列の値のコピーを作成できます。

var arr2 = arr.concat();

4)メソッドを文字列化して解析することも推奨されていませんが、配列とオブジェクトをコピーする簡単な方法です。

var arr2 = JSON.parse(JSON.stringify(arr));

5)Array.fromメソッド。これは広くサポートされていません。使用する前に、さまざまなブラウザーでサポートを確認してください。

const arr2 = Array.from(arr);

6)ECMA6方法、これも完全にはサポートされていませんが、トランスパイルする場合は、belbelJが役立ちます。

const arr2 = [...arr];

6
let a = [1,2,3];

これで、次のいずれかを実行して配列のコピーを作成できます。

let b = Array.from(a); 

または

let b = [...a];

または

let b = new Array(...a); 

または

let b = a.slice(); 

または

let b = a.map(e => e);

今、私が変更した場合、

a.push(5); 

次に、aは[1,2,3,5]ですが、参照が異なるため、bは[1,2,3]のままです。

しかし、私は、上記のすべてのメソッドで Array.fromの 方が優れていて、主に配列をコピーするために作成されていると思います


1
どちらが最速ですか?
マークフレーム


4

私の特定のケースでは、配列が損なわれていないことを確認する必要があったので、これは私にとってうまくいきました:

// Empty array
arr1.length = 0;
// Add items from source array to target array
for (var i = 0; i < arr2.length; i++) {
    arr1.push(arr2[i]);
}

2
+1は、まったく同じことを行う関数を呼び出すことによってコードに不明瞭さを追加しないが、あまり明確ではありません。スライスは内部的にはより効率的ですが、コードに取り組んでいる誰にとっても、これはあなたの意図を示しています。さらに、コピーするものを(たとえば)フィルタリングする場合に、後で最適化するのが簡単になります。ただし、これはディープコピーを処理せず、同じ内部オブジェクトが参照によって新しい配列に渡されることに注意してください。これはあなたがやりたいことかもしれませんが、そうでないかもしれません。
同期されていない2014年

4

多次元配列/オブジェクトのコピーを作成します。

function deepCopy(obj) {
   if (Object.prototype.toString.call(obj) === '[object Array]') {
      var out = [], i = 0, len = obj.length;
      for ( ; i < len; i++ ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   if (typeof obj === 'object') {
      var out = {}, i;
      for ( i in obj ) {
         out[i] = arguments.callee(obj[i]);
      }
      return out;
   }
   return obj;
}

この機能を提供してくれたJames Padolseyに感謝します。

出典:こちら


3

ダン、派手なトリックを使う必要はありません。これを行うことで、arr1のコピーを作成するだけです。

var arr2 = new Array(arr1);

現在arr1arr22つの異なる配列変数が別々のスタックに格納されています。 これをjsfiddleで確認してください


これは配列をコピーしません。元の(つまりvar arr2 = [arr1];)を参照する1つの要素を持つ配列を作成します。
Timothy003 2018年

3

代入演算子(=)を使用して配列をコピーする場合、コピーは作成されず、配列へのポインター/参照がコピーされるだけです。例えば:

const oldArr = [1,2,3];

const newArr = oldArr;  // now oldArr points to the same place in memory 

console.log(oldArr === newArr);  // Points to the same place in memory thus is true

const copy = [1,2,3];

console.log(copy === newArr);  // Doesn't point to the same place in memory and thus is false

多くの場合、データを変換するときは、初期のデータ構造(配列など)をそのまま維持したい場合があります。これを行うには、配列の正確なコピーを作成します。これにより、最初の配列をそのままにして、この配列を変換できます。

配列をコピーする方法:

const oldArr = [1,2,3];

// Uses the spread operator to spread out old values into the new array literal
const newArr1 = [...oldArr];

// Slice with no arguments returns the newly copied Array
const newArr2 = oldArr.slice();

// Map applies the callback to every element in the array and returns a new array
const newArr3 = oldArr.map((el) => el);

// Concat is used to merge arrays and returns a new array. Concat with no args copies an array
const newArr4 = oldArr.concat();

// Object.assign can be used to transfer all the properties into a new array literal
const newArr5 = Object.assign([], oldArr);

// Creating via the Array constructor using the new keyword
const newArr6 = new Array(...oldArr);

// For loop
function clone(base) {
	const newArray = [];
    for(let i= 0; i < base.length; i++) {
	    newArray[i] = base[i];
	}
	return newArray;
}

const newArr7 = clone(oldArr);

console.log(newArr1, newArr2, newArr3, newArr4, newArr5, newArr6, newArr7);

配列またはオブジェクトがネストされている場合は注意してください!:

配列がネストされている場合、値は参照によってコピーされます。これが問題を引き起こす可能性のある例を次に示します。

let arr1 = [1,2,[1,2,3]]

let arr2 = [...arr1];

arr2[2][0] = 5;  // we change arr2

console.log(arr1);  // arr1 is also changed because the array inside arr1 was copied by reference

したがって、コピーする配列内にオブジェクトまたは配列がある場合は、これらのメソッドを使用しないでください。つまり、これらのメソッドはプリミティブの配列でのみ使用します。

JavaScript配列をディープクローンしたい場合はJSON.parse、と組み合わせて次のJSON.stringifyように使用します。

let arr1 = [1,2,[1,2,3]]

let arr2 = JSON.parse(JSON.stringify(arr1)) ;

arr2[2][0] = 5;

console.log(arr1);  // now I'm not modified because I'm a deep clone

コピーのパフォーマンス:

したがって、最適なパフォーマンスを得るためにどちらを選択するか。最も冗長な方法であるforループは、最高のパフォーマンスを発揮します。for本当にCPUを多用するコピー(大規模/多数の配列)にはループを使用します。

その後、この.slice()メソッドもまともなパフォーマンスを発揮し、プログラマーが実装するのに冗長性が少なく、簡単になります。.slice()CPUにあまり負荷をかけない、毎日の配列のコピーに使用することをお勧めします。またJSON.parse(JSON.stringify(arr))、ディープクローンが不要でパフォーマンスが問題となる場合は、(大量のオーバーヘッド)の使用を避けます。

ソースパフォーマンステスト


3

あなたの配列は、の要素が含まれている場合、プリミティブデータ型などなどint型、char型、または文字列を、その後することができますもの、例えば.slice()または.MAP(と、元の配列のコピーを返すメソッド)や拡散オペレータのユーザー1( ES6に感謝)。

new_array = old_array.slice()

または

new_array = old_array.map((elem) => elem)

または

const new_array = new Array(...old_array);

しかし、あなたの配列が含まれている場合は、複雑な要素などのオブジェクト(または配列)以上のようにネストされたオブジェクトを、そして、あなたは、内側の最後のレベル、他の参照に、トップレベルからのすべての要素のコピーを作成していることを確認する必要がありますオブジェクトが使用されます。つまり、new_arrayのobject_elementsの値を変更しても、old_arrayに影響します。 old_arrayのDEEP COPYを作成するときに、各レベルでこのコピーのメソッドを呼び出すことができます。

ディープコピーの場合、データのタイプに応じて各レベルのプリミティブデータタイプに対して上記の方法を使用できます。または、このコストのかかる方法(下記参照)を使用して、多くの作業を行わずにディープコピーを作成できます。

var new_array = JSON.parse(JSON.stringify(old_array));

他にも、要件に応じて使用できる方法がたくさんあります。配列を値によって他の配列にコピーしようとしたときに何が起こるかをおおまかに説明するために、それらの一部のみを述べました。


どうもありがとうございました。あなたの答えは私のシナリオで働いた唯一の人でした
albert sh

2

オブジェクトまたは配列の新しいコピーを作成する場合は、オブジェクトのプロパティまたは配列の要素を明示的にコピーする必要があります。次に例を示します。

var arr1 = ['a','b','c'];
var arr2 = [];

for (var i=0; i < arr1.length; i++) {
   arr2[i] = arr1[i];
}

不変のプリミティブ値と変更可能なオブジェクト参照について、Googleで詳細情報を検索できます。


1
配列のオブジェクトのプロパティを明示的にコピーする必要はありません。Chtiwi Malekの回答を参照してください。
Magne



2

コピーする方法がいくつかあります。

const array = [1,2,3,4];

const arrayCopy1 = Object.values(array);
const arrayCopy2 = Object.assign([], array);
const arrayCopy3 = array.map(i => i);
const arrayCopy4 = Array.of(...array );



2

簡単な例:

  1. 配列の要素がプリミティブ型(文字列、数値など)の場合

var arr1 = ['a','b','c'];
// arr1 and arr2 are independent and primitive elements are stored in 
// different places in the memory
var arr2 = arr1.slice(); 
arr2.push('d');
console.log(arr1); // [ 'a', 'b', 'c' ]
console.log(arr2); // [ 'a', 'b', 'c', 'd' ]

  1. 配列の要素がオブジェクトリテラルの場合、別の配列({}、[])

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
// arr1 and arr2 are independent and reference's/addresses are stored in different
// places in the memory. But those reference's/addresses points to some common place
// in the memory.
var arr2 = arr1.slice(); 
arr2.pop();      // OK - don't affect arr1 bcos only the address in the arr2 is
                 // deleted not the data pointed by that address
arr2[0].x = 'z'; // not OK - affect arr1 bcos changes made in the common area 
                 // pointed by the addresses in both arr1 and arr2
arr2[1][0] = 9;	 // not OK - same above reason

console.log(arr1); // [ { x: 'z', y: 'b' }, [ 9, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]

  1. 2の解決策:要素ごとのディープコピー

var arr1 = [{ x: 'a', y: 'b'}, [1, 2], [3, 4]];
arr2 = JSON.parse(JSON.stringify(arr1));
arr2.pop();	  // OK - don't affect arr1
arr2[0].x = 'z';  // OK - don't affect arr1
arr2[1][0] = 9;	  // OK - don't affect arr1

console.log(arr1); // [ { x: 'a', y: 'b' }, [ 1, 2 ], [ 3, 4 ] ]
console.log(arr2); // [ { x: 'z', y: 'b' }, [ 9, 2 ] ]


1

バリアントは次のとおりです。

var arr1=['a', 'b', 'c'];
var arr2=eval(arr1.toSource());
arr2.push('d');
console.log('arr1: '+arr1+'\narr2: '+arr2);
/*
 *  arr1: a,b,c
 *  arr2: a,b,c,d
 */

それほど悪い考えではありませんが、evalの代わりにJSONのstringify / parseを使用するほうがよいでしょう。さらに、別のjsPerfの比較もチェックアウトに適しています。また、toSource標準ではなく、たとえばChromeでは機能しないことに注意してください。
dmi3y 2014年

1

新しく導入されたがありますArray.fromが、残念ながら、この記事の執筆時点では、最新のFirefoxバージョン(32以降)でのみサポートされています。次のように簡単に使用できます。

var arr1 = [1, 2, 3];
console.log(Array.from(arr1)); // Logs: [1, 2, 3]

参照:ここ

またはArray.prototype.map、識別関数とともに使用できます。

function identity(param)
{
    return param;
}

var arr1 = [1, 2, 3],
    clone = arr1.map(identity);

参照:ここ


言及のための+1 Array.from。これは現在、Internet Explorer(ソース)を除くすべての主要なブラウザーでサポートされています。。
mgthomas99 2017年

Array.fromデータを変更することに注意してください。ディープコピー/ディープクローニングは提供されません。
Sudhansu Choudhary

1

オブジェクトを含むES6配列の場合

cloneArray(arr) {
    return arr.map(x => ({ ...x }));
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.