javascript 配列をディープコピーする
- 作成日 2022.10.17
- 更新日 2022.10.28
- javascript
- javascript
javascriptで、配列をディープコピーするサンプルコードを記述してます。具体的に配列をディープコピーする方法はページの真ん中の方にあります。「JSON.stringify()」と「JSON.parse()」を使用してます。
- OS windows11 pro 64bit
- ブラウザ chrome 107.0.5304.63
配列をコピー
配列の場合は、ただ変数を代入すると元の値が変更されると、メモリ上の同じデータを参照しているので、代入された配列の値も変更されます。
const arr = ['a', 'b'];
const new_arr = arr;
arr.push('c'); // 値を追加
console.log(arr); // ['a', 'b', 'c']
console.log(new_arr); // ['a', 'b', 'c']
元の値に影響しないようにするには、スプレッド構文を使用します。
const arr = ['a', 'b'];
const new_arr = [...arr];
arr.push('c'); // 値を追加
console.log(arr); // ['a', 'b', 'c']
console.log(new_arr); // ['a', 'b']
また、スプレッド構文以外には、以下の方法があります。
const arr = ['a', 'b'];
const new_arr1 = Array.from(arr); // 配列化
const new_arr2 = [].concat(arr); // 空の配列と結合
const new_arr3 = arr.slice(); // 範囲を指定しないでスライス
arr.push('c'); // 値を追加
console.log(arr); // ['a', 'b', 'c']
console.log(new_arr1); // ['a', 'b']
console.log(new_arr2); // ['a', 'b']
console.log(new_arr3); // ['a', 'b']
パフォーマンスは「concat」か「slice」がいいです。
実行回数:100000回 関数名:スプレッド構文 実行時間:371(ms)
実行回数:100000回 関数名:Array.from 実行時間:228(ms)
実行回数:100000回 関数名:concat 実行時間:6.90(ms)
実行回数:100000回 関数名:slice 実行時間:9.30(ms)
ただし、配列の中に配列やオブジェクトがあり階層が2階層になっている場合は「ディープコピー」する必要があります。
const arr = [['aaa'],{'key':'aaa'}];
const new_arr1 = Array.from(arr); // 配列化
const new_arr2 = [].concat(arr); // 空の配列と結合
const new_arr3 = arr.slice(); // 範囲を指定しないでスライス
const new_arr4 = [...arr]; // スプレッド構文
arr[0].push("bbb");
arr[1].key = "bbb"
console.log(arr); // [['aaa', 'bbb'],{key: 'bbb'}]
console.log(new_arr1); // [['aaa', 'bbb'],{key: 'bbb'}]
console.log(new_arr2); // [['aaa', 'bbb'],{key: 'bbb'}]
console.log(new_arr3); // [['aaa', 'bbb'],{key: 'bbb'}]
console.log(new_arr4); // [['aaa', 'bbb'],{key: 'bbb'}]
配列をディープコピー
ディープコピーする場合は「JSON.stringify()」で文字列化した後に、「JSON.parse()」でまたオブジェクト化します。
const arr = [['aaa'],{'key':'aaa'}];
const new_arr = JSON.parse( JSON.stringify(arr) );
arr[0].push("bbb");
arr[1].key = "bbb"
console.log(arr); // [['aaa', 'bbb'],{key: 'bbb'}]
console.log(new_arr); // [['aaa'],{'key':'aaa'}];
ただし、上記の方法は「new Date()」や「undefined」や「関数」に使用するとおかしな挙動になります。
const arr = [[new Date()],{'key':undefined}];
const new_arr = JSON.parse( JSON.stringify(arr) );
arr[0].push("bbb");
arr[1].key = "bbb"
console.log(arr);
console.log(new_arr);
実行結果
「map」を使用して条件ごとに処理を適応すると、期待する動作になります。
const arr = [[new Date()],{'key1':undefined},{'key2':function(){}}];
const new_arr = arr.map( v => {
if( Array.isArray(v) ){
return [ ...v ]
}else if( typeof(v) == "object" ){
return { ...v }
}else return v
} );
arr[0].push("bbb");
arr[1].key1 = "bbb"
arr[2].key2 = "bbb"
console.log(arr);
console.log(new_arr);
実行結果
外部ライブラリ「lodash」を使用することも可能です。
サンプルコード
以下は、
「ディープコピー」ボタンをクリックすると、ランダムに生成した配列をディープコピーして、コピー元を再度ランダムに生成した配列に変更して双方の値を表示する
サンプルコードとなります。
※cssには「tailwind」を使用してます。関数はアロー関数を使用してます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>mebeeサンプル</title>
<!-- MDB -->
<link href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/4.2.0/mdb.min.css" rel="stylesheet" />
</head>
<style>
.main {
margin: 0 auto;
margin-top: 200px;
display: flex;
flex-direction: column;
align-items: center;
font-size: 30px;
}
</style>
<script>
const hoge = () => {
// ランダムな3つの値がある配列を生成
let arr1 = radarr(3);
// 生成した配列をdedpコピー
let arr2 = arr1.map(v => {
if (Array.isArray(v)) {
return [...v]
} else if (typeof (v) == "object") {
return { ...v }
} else return v
});
// もう一度、ランダムな3つの値がある配列を生成
arr1 = radarr(3);
// arr1表示
disp(arr1, "txt1");
// arr2表示
disp(arr2, "txt2");
}
const radarr = (len) => {
//ランダムな9までの配列を生成
let arr = [];
let num = 10;
let length = len;
for (let i = 0; i < length; i++) {
arr.push(Math.floor(Math.random() * num));
}
return arr;
}
//フロントに表示する関数
const disp = (arr, id) => {
let text = [];
// for ofを使用
for (let item of arr) {
text.push('<li class="list-group-item">' + item + '</li>');
}
//innerHTMLを使用して表示
document.getElementById(id).innerHTML = text.join('');
}
window.onload = () => {
// ボタンを取得
let elmbtn = document.getElementById('btn');
// クリックイベントを登録
elmbtn.onclick = () => {
hoge();
};
}
</script>
<body>
<div class="main">
<h2 class="badge badge-success">配列を生成</h2>
<ul id="txt1" class="list-group"></ul>
<h2 class="badge badge-success">配列をディープコピー</h2>
<ul id="txt2" class="list-group"></ul>
<button id="btn" type="button" class="btn btn-raised btn-danger">
ディープコピー
</button>
</div>
</body>
</html>
ディープコピーなので元の値のみが変更されていることが確認できます。
-
前の記事
MariaDB 年月日から月のみを取得する 2022.10.17
-
次の記事
Tauri ビルド時にエラー「Error You must change the bundle identifier in tauri.conf.json > tauri > bundle > identifi. The default value com.tauri.de is not allowed as it must be unique across applications.」が発生した場合の対処法 2022.10.17
コメントを書く