-ao- ramune blog

©2020 unio / GO2直営からふるラムネ

JavaScriptでプリミティブ型の配列をディープコピーする

2019年09月24日
  • 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
                
            
プロフィール画像
なかのひと:unio

数十年前の牧歌的なインターネッツが好きだった、永遠のモラトリアム人。 ただ、モラトリアムしててもお金は増えないので、しゃかいの厳しさを斜め後ろから眺めつつほそぼそと生活しています。

Twitter GitHub
[広告]