JavaScript 再入門(その12) 配列の複製

= だけで代入して複製

配列を = で代入すると、どちらか一方の要素の値が変わると双方の値が変わります。

let ary = [ '車寅次郎' , '諏訪さくら' , '諏訪博' , '諏訪満男' , 'タコ社長' , '御前様' ] ;
let copyAry = ary ; // イコールで代入
copyAry[0] = '源吉' ; // 代入先の要素を変更
// 代入元の要素の値も変わる。
console.log( ary ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']
console.log( copyAry ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']

concat で複製

concat メソッドは配列を連結して新たな配列を返します。
引数を指定せずに concat を実行すると自身の配列だけが新たな配列として返されます。

let ary = [ '車寅次郎' , '諏訪さくら' , '諏訪博' , '諏訪満男' , 'タコ社長' , '御前様' ] ;
let copyAry = ary.concat() ; // concatで新しい配列を作成して複製
copyAry[0] = '源吉' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ; // (6) ['車寅次郎', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']
console.log( copyAry ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']

slice で複製

slice メソッドは引数で指定された要素を配列として返します。
引数を指定せずに slice を実行すると自身の配列だけが新たな配列として返されます。

let ary = [ '車寅次郎' , '諏訪さくら' , '諏訪博' , '諏訪満男' , 'タコ社長' , '御前様' ] ;
let copyAry = ary.slice() ; // slice で新しい配列を作成して複製
copyAry[0] = '源吉' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ; // (6) ['車寅次郎', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']
console.log( copyAry ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']

…Array で複製

…Array スプレッド構文*ES2015 を利用すると、もとの配列要素を展開して新しい配列を作成します。

let ary = [ '車寅次郎' , '諏訪さくら' , '諏訪博' , '諏訪満男' , 'タコ社長' , '御前様' ] ;
let copyAry = [...ary] ; // スプレッド構文 で新しい配列を作成して複製
copyAry[0] = '源吉' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ; // (6) ['車寅次郎', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']
console.log( copyAry ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']

Array.from で複製

Array.from*ES2015 を利用すると、もとの配列から新しい配列を作成します。

let ary = [ '車寅次郎' , '諏訪さくら' , '諏訪博' , '諏訪満男' , 'タコ社長' , '御前様' ] ;
let copyAry = Array.from(ary) ; // Array.from で新しい配列を作成して複製
copyAry[0] = '源吉' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ; // (6) ['車寅次郎', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']
console.log( copyAry ) ; // (6) ['源吉', '諏訪さくら', '諏訪博', '諏訪満男', 'タコ社長', '御前様']

シャローコピー(浅い複製)とディープコピー(深い複製)

concat / slice / スプレッド構文 / Array.from での複製はシャローコピー(浅い複製)となるので、多次元の配列の複製には注意が必要です。

let ary =  [
	[ '車寅次郎' , '渥美清' ] ,
	[ '諏訪さくら' , '倍賞千恵子' ] ,
	[ 'タコ社長' , '太宰久雄' ] ,
] ;

let copyAry = [...ary] ; // スプレッド構文 で新しい配列を作成して複製
copyAry[0][0] = '源吉' ; // 複製先の要素を変更
copyAry[0][1] =  '佐藤蛾次郎' ; // 複製先の要素を変更
// 複製元の要素の値も変わる。 (車寅次郎はどこへ行った...)
console.log( ary ) ;
/*
0: (2) ['源吉', '佐藤蛾次郎'] <-- 要素の値が変わってしまった。
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/
console.log( copyAry ) ;
/*
0: (2) ['源吉', '佐藤蛾次郎']
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/

JSON.parse(JSON.stringify()) で文字列に変換して複製

JSON.stringify() で配列を文字列に変換して、文字列を JSON.parse() を使って再び配列に変換するとディープコピー(深い複製)となります。
但し複製元の配列がJSONで表現可能でなければなりません。したがって要素内に undefined や関数オブジェクトなどが含まれていると複製できません。

let ary =  [
	[ '車寅次郎' , '渥美清' ] ,
	[ '諏訪さくら' , '倍賞千恵子' ] ,
	[ 'タコ社長' , '太宰久雄' ] ,
] ;

let copyAry = JSON.parse(JSON.stringify(ary)) ; // JSON で新しい配列を作成して複製
copyAry[0][0] = '源吉' ; // 複製先の要素を変更
copyAry[0][1] =  '佐藤蛾次郎' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ;
/*
0: (2) ['車寅次郎', '渥美清']
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/
console.log( copyAry ) ;
/*
0: (2) ['源吉', '佐藤蛾次郎']
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/

JavaScriptにはディープコピーの方法が提供されていない。

JavaScriptではディープコピーの方法は提供されていないので、自作で実装する必要があります。

let ary =  [
	[ '車寅次郎' , '渥美清' ] ,
	[ '諏訪さくら' , '倍賞千恵子' ] ,
	[ 'タコ社長' , '太宰久雄' ] ,
] ;
/* 二次元配列の複製
----------------------------------*/
function copyArray( arg ) {
	let resArg = [] ;
	for( const item of arg ) {
		resArg.push( Array.from( item ) ) ;
	}
	return resArg ;
}

let copyAry = copyArray(ary) ; // 自作の複製関数で新しい配列を作成して複製
copyAry[0][0] = '源吉' ; // 複製先の要素を変更
copyAry[0][1] =  '佐藤蛾次郎' ; // 複製先の要素を変更
// 複製元の要素の値は変わらない。
console.log( ary ) ;
/*
0: (2) ['車寅次郎', '渥美清']
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/
console.log( copyAry ) ;
/*
0: (2) ['源吉', '佐藤蛾次郎']
1: (2) ['諏訪さくら', '倍賞千恵子']
3: (2) ['タコ社長', '太宰久雄']
*/

参考:
MDN 開発者向けのウェブ技術 > Array
MDN 開発者向けのウェブ技術 > concat
MDN 開発者向けのウェブ技術 > slice
MDN 開発者向けのウェブ技術 > スプレッド構文
MDN 開発者向けのウェブ技術 > Array.from
MDN 開発者向けのウェブ技術 > JSON.parse
MDN 開発者向けのウェブ技術 > JSON.stringify
MDN 開発者向けのウェブ技術 > JavaScript「再」入門