もじれつとがめん

プログラムの忘備録とゲームとかその他色々。

配列・連想配列の複製(コピー)で引っかかった

連想配列を別の新しい変数に代入して操作したら元の連想配列が操作された

作業の中で連想配列を別の新しい変数に入れる必要があって、単純に初期化と同時にやってみると問題が起きた。

配列・連想配列の代入は参照渡しらしい

当初問題が起きたコードがこれ
[javascript] //最初の配列 var array_1 = { 'yasai':{'imo':1, 'nagaimo':2, 'imoimo':99}, 'niku':4, 'susi':0}; var array_2 = {}; //代入 array_2 = array_1;

//操作 array_2['yasai']['imo'] = 99;

//99が表示される console.log(array_2['yasai']['imo']);

// 1 ではなくて、99が表示される?!!?! console.log(array_1['yasai']['imo']); [/javascript] ちなみにこれも同じ事 [javascript] //最初の配列 var array_1 = { 'yasai':{'imo':1, 'nagaimo':2, 'imoimo':99}, 'niku':4, 'susi':0}; var array_2 = {};

//代入 array_2['yasai'] = array_1['yasai']; array_2['niku'] = array_1['niku']; array_2['susi'] = array_1['susi'];

//99が表示される console.log(array_2['yasai']['imo']);

// 1 ではなくて、99が表示される?!!?! console.log(array_1['yasai']['imo']); [/javascript]

この事からどうやらjavascriptでは連想配列を別の要素に代入すると参照渡しになるらしい。

参照渡しとは

参照渡しとはメモリのさしてる場所を代入するので、渡された側にあるのは元データの保存されてるメモリの場所があるだけ。
つまりその場所にたいして操作をしてしまうので元が変更されてしまう。
説明してて分かりづらいのでもう少し簡単に説明すると、 [javascript] var array_1 = {'imo':0, 'niku':1}; var array_2 = {}; var array_3 = {};

//参照渡し //これで array_2,array_3 はarray_1を"参照"しているだけ。 //参照しているだけなので、要素自体を保持してない。シンボリックリンクみたいなイメージ array_2 = array_1; array_3 = array_3;

//参照されているarray_1に要素を追加 array_1['susi'] = 99;

//参照しているので当然 array_2, array_3も影響を受ける //99が表示される console.log(array_2['susi']); console.log(array_3['susi']);

//同じくarray_1以外に操作を加えると、array_1に直接そうさするのと同義 array_3['mizu'] = 100; //100が表示される console.log(array_1['mizu']); console.log(array_2['mizu']); [/javascript] こんな感じになる。
ようは、参照しているだけなのでその変数自体には何も値を保持してない。
なので、その変数に操作を加える事は参照してる元に操作を加える事になる。
こう考えると、シンボリックリンクみたいなイメージが一番近いかも。

ちなみに配列も同じく参照渡しになる。
[javascript] var array_1 = ['imo','susi','mizu']; var array_2 = ; //代入してみる array_2 = array_1; //要素を追加してみると... array_2.push('niku'); //array_1にも追加されている... console.log(array_1[3]); [/javascript]

では、純粋に複製したい時はどうすれば良いのか。

$.extend() をつかう

jQueryライブラリを使用しているなら extend を使用する事で一発で出来る。
[javascript] var array_1 = { 'yasai':{'imo':1, 'nagaimo':2, 'imoimo':99}, 'niku':4, 'susi':0}; var array_2 = $.extend(true, {}, array_1);

//操作しても array_2['mizu'] = 99; //大元には影響しない //undifind console.log(array_1['mizu']); [/javascript]

ただの配列なら [javascript] var array_1 = ['imo','susi','mizu']; var array_2 = $.extend(true, , array_1); [/javascript] このように一行で解決するのでjQueryが使えるなら簡単。

ループを使って要素を一つずつ抜き出す

jQueryが使用できないなら要素を一つずつ抜き出すしかない。
どうやら、純粋に要素が文字列や数値なら参照渡しではなく普通の代入(複製)になるようなので配列や連想配列を、ループを使って要素一つ一つを代入していくしか無い。 よって、コピーしたい配列・連想配列によって処理が異なるので注意。
多次元ならその分だけループを追加しなければならないのでちょっと大変。 [javascript] //一次元の連想配列・配列の場合 var array_1 = { 'yasai':0, 'niku':4, 'susi':0}; var array_2 = {} for(var key in array_1){ array_2[key] = array_1[key]; }

//新しく代入しても array_2['mizu'] = 99; //問題無し //undifind console.log(array_1['mizu']); [/javascript]

多次元な配列・連想配列ならばこの最初のループの中でさらにまた要素を判別してループさせる必要があるので注意が必要。

終わりに

言語によってここら辺の動きが違うので注意が必要かな。 phpperlからjavascriptさわったからついね...