#この記事でしていること
配列の中にハッシュを入れる際、奇妙な挙動をしたのでそれについて検証しました。
具体的には配列に対してハッシュを追加した後、ハッシュの中身を変更して再度配列に追加しました。
中身を変更する前に空のハッシュを再宣言しなかったらどうなるか?が今回の記事になります。
#経緯
Rubyの簡単な記述を練習していた時に
- 配列を宣言
- ハッシュを宣言
- ハッシュに中身を追加
- ハッシュを配列に入れる
- 空のハッシュを再度宣言
- ハッシュに中身を追加
- ハッシュを配列に入れる
という一連の流れの中で「5.は削れるのでは?」と思い実際に削ってみました。
すると期待している動きと違っていた・・・どころか謎の挙動をしていたので、検証してみました。
#再現
###Before
配列の中にハッシュを入れる際は下記のような手順を踏んでいくことが一般的かと思います。
# 空の配列とハッシュを宣言する
array = []
hash = {}
# ハッシュに【キー:num 値:1】を入れ、ハッシュを配列に入れる
hash[:num] = 1
array << hash
# 再度空のハッシュを宣言し、同じキーに別の値「2」を入れる
hash = {}
hash[:num] = 2
array << hash
# 配列の中身を出力
puts array
$ ruby test.rb
{:num=>1}
{:num=>2}
これが通常の結果です。
配列の中に一個目と二個目のハッシュが入っていることがわかります。
###After
続いて空のハッシュの宣言を抜いてみます。
array = []
hash = {}
hash[:num] = 1
array << hash
# ここでハッシュを宣言しない
hash[:num] = 2
array << hash
puts array
#####期待していた結果
$ ruby test.rb
{:num=>1}
{:num=>2}
#####実際の結果
$ ruby test.rb
{:num=>2}
{:num=>2}
前に入れた「1」はどこいった。
そしてなんで「2」は2つも入っとるんや・・・
#検証
###確認
まずは下記の時にどのような値が入っているのか確認します。
[1.] 1つ目のハッシュを配列に入れた時
[2.] ハッシュの中身を入れ替えた時
[3.] 中身を入れ替えたハッシュを再び配列に入れた時
array = []
hash = {}
hash[:num] = 1
array << hash
puts array # [1.]
puts "---------------"
hash[:num] = 2
puts array # [2.]
puts "---------------"
array << hash
puts array # [3.]
puts "---------------"
$ ruby test.rb
{:num=>1}
---------------
{:num=>2}
---------------
{:num=>2}
{:num=>2}
---------------
[2.]の結果を見てびっくりしました。
えっ・・・一度配列に入れたのにハッシュの中身を入れ替えたら配列の中の方も変わってしまうの・・・?
そして[3.]の結果を見ていただくとわかる通り、配列の追加をすると同じ中身でも配列内に積載されるため、結果として「{:num=>2}」が2つも入ってしまっていたようです。
###検証
結果は理解したけどまだちょっと納得いかない!
ということで検証してみました。
####検証1:空のハッシュを使わない
array = []
hash = {:num => 1}
array << hash
puts array
puts "---------------"
hash = {:num => 2}
puts array
puts "---------------"
array << hash
puts array
puts "---------------"
$ ruby test.rb
{:num=>1}
---------------
{:num=>1}
---------------
{:num=>1}
{:num=>2}
---------------
・・・あれ?うまくいったぞ
####検証2:変数
array = []
num = 1
array << num
puts array
puts "---------------"
num = 2
puts array
puts "---------------"
array << num
puts array
puts "---------------"
$ ruby test.rb
1
---------------
1
---------------
1
2
---------------
変数も期待通りの動きだ・・・
#考察
今回は配列の中身をハッシュと変数で試しましたが、検証の結果を踏まえた仮説は以下になります。
hash[:すでに配列に入っているハッシュのキー] = 値
→すでに宣言されているハッシュのキーの値の『変更』という扱いになってしまう
hash = {:すでに配列に入っているハッシュのキーと同名のキー => 値}
変数 = 値
→上記はどちらもその都度ハッシュまたは変数を『新しく宣言』している扱いになるため、すでに宣言されたハッシュまたは変数の値には影響しない
上記の違いがあったために期待した結果にならなかったのだと思いました。
#最後に
今回の内容はあえて通常しない記述をしているため、実務においてはあまり問題になることはないと思われますがQiitaの投稿練習も兼ねてまとめてみました。
内容に誤りがある場合やさらに詳しい情報をお持ちの方がいらっしゃいましたら教えていただけると嬉しいです。