JavaScriptでプリミティブ型の配列をディープコピーする
プリミティブ型1次元配列のディープコピー
Array.from を使うと、文字列や数値などの プリミティブ型 配列をディープコピーできます。
Array.from(ES6のみ)
const targets = [1, 2, 3];
const copied = Array.from(targets);
// 元の配列の0番目を書き換える
targets[0] = "replaced";
// プリミティブ値なので、コピー先には影響しない
console.log(copied[0]);
> 1
プリミティブ型多次元配列のディープコピー
Array.fromは低コストで配列をコピーできますが、多次元配列だとうまく行きません。
Array.from(ES6のみ)
const targets = [
[1, 2],
[3, 4]
];
const copied = Array.from(targets);
// 2次元配列の値を書き換える
targets[0][0] = "replaced";
// 配列なのでArray.fromだと参照コピーされる
console.log(copied[0][0]);
> replaced
再帰関数か、JSON.stringify/parseを使うと、 プリミティブ型の多次元配列をディープコピーできます。
プリミティブ型多次元配列のディープコピー
// 再帰関数パターン
function doRecursiveFunction(matrix) {
return matrix.map(row => {
if (Array.isArray(row)) {
return doRecursiveFunction(row);
}
return row;
});
}
// JSON.stringify/parseパターン
function doJSON(matrix) {
return JSON.parse(JSON.stringify(matrix));
}
JSON.stringify/parseパターンは、配列をJSON文字列に変換して再度配列に戻すパターンです。 非常にシンプルで分かりやすいのですが、処理速度は再帰関数に比べると数倍遅いです。
処理速度比較
const targets = [];
for (let i = 0; i < 100; i++) {
targets.push([1, 2, 3, 4, 5]);
}
let start = Date.now();
for (let i = 0; i < 100000; i++) {
doRecursiveFunction(targets);
}
console.log("doRecursiveFunction: " + (Date.now() - start) + " msec");
start = Date.now();
for (let i = 0; i < 100000; i++) {
doJSON(targets);
}
console.log("doJSON: " + (Date.now() - start) + " msec");
> doRecursiveFunction: 605 msec
> doJSON: 2678 msec
一般的に再帰関数も高コストなので、多次元配列の構造が判明している場合はArray.fromで組んだ方が高速に動作します。
2次元配列をArray.fromでディープコピー
// Array.fromパターン
function doArrayFrom(matrix) {
return matrix.map(row => Array.from(row));
}
const targets = [];
for (let i = 0; i < 100; i++) {
targets.push([1, 2, 3, 4, 5]);
}
let start = Date.now();
for (let i = 0; i < 100000; i++) {
doArrayFrom(targets);
}
console.log("doArrayFrom: " + (Date.now() - start) + " msec");
> doArrayFrom: 248 msec