0
0

More than 3 years have passed since last update.

JavaScriptの配列操作でハマった話

Last updated at Posted at 2020-12-28

つまらないことでハマったので備忘録を残します。

やりたいこと

長さの決まった2次元配列(arr)にarr[index]でアクセスして値を更新したい。

問題が起きるコード

(function(size){
    const arr = new Array(size).fill([]);

    // arr[index]にアクセスする何らかの処理
    for(let i=0;i<arr.length;i++){
        arr[i].push(i);
    }
    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
})(10)

想像していた出力は以下の通りです。

// debugger eval code
Array [ 0 ]
Array [ 1 ]
Array [ 2 ]
Array [ 3 ]
Array [ 4 ]
Array [ 5 ]
Array [ 6 ]
Array [ 7 ]
Array [ 8 ]
Array [ 9 ]

しかし、得られた出力はこちらです。

// debugger eval code
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Array(10) [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

原因

完全に私の理解度の問題でArray.prototype.fillの挙動を勘違いしていました。
よく考えれば当たり前ですが、fillメソッドでは再帰的に新しいインスタンスを作成するのではなく、単純にオブジェクトの代入を行います。
上記のコードでは初期化部分に問題があり、初期化部分は以下のコードと等価になります。

// 問題の起きるコード
const arr = new Array(size).fill([]);

// 問題の起きるコードと等価のコード
// size = 10
const ref = [];
const arr = [ ref, ref, ref, ref, ref, ref, ref, ref, ref, ref ];

基本仕様として、JavaScriptは値(number | string | boolean など)は値の代入、オブジェクトは参照を代入します。
上記のコードで何が起きるかというと、arr[index]で参照される値はすべてrefとなってしまうためarr[index].pushした場合は要素全てに再帰的にpushしたような挙動になります。

解決策

インスタンスを新しく作成すれば問題を回避できるのでArray.prototype.mapArray.prototype.filterを使用すれば回避できます。(もっと簡潔な解決策があれば教えて下さい...)

// 抜粋
const arr = new Array(size).fill(0).map(n => []);
(function(size){
    const arr = new Array(size).fill(0).map(n => []);

    // arr[index]にアクセスする何らかの処理
    for(let i=0;i<arr.length;i++){
        arr[i].push(i);
    }
    for(let i=0;i<arr.length;i++){
        console.log(arr[i]);
    }
})(10)

参考

[1] MDN, Array, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array
[2] MDN, Array.prototype.fill, https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/fill

0
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0