QKAKE::TECH

テックなことを書く

【Ruby】連想配列(ハッシュ)の値を複数個設定したいとき

pry(main)> users = Hash.new
=> {}
pry(main)> users[:male]
=> nil
pry(main)> users[:male] = "Yuji"
=> "Yuji"
pry(main)> users[:male]
=> "Yuji"
pry(main)> users
=> {:male=>"Yuji"}

ユーザの名前を管理する連想配列(ハッシュ)をつくり、 male という key で "Yuji" という値を代入したらこうなる。 次に同じ key の male"Taro" を代入すると次のようになる。

pry(main)> users[:male] = "Taro"
=> "Taro"
pry(main)> users
=> {:male=>"Taro"}

あたらしく別のオブジェクトとして {:male=>"Taro"} という連想配列が出来上がっている。 このように連想配列はひとつの key に対してひとつの value を取るような機構になっている。

そこで、ひとつの key に対して複数の value を持ちたい場合は value を配列でとるといい。

pry(main)> users[:male] = ["Yuji", "Taro"]
=> ["Yuji", "Taro"]                  
pry(main)> users
=> {:male=>["Yuji", "Taro"]}
pry(main)> users[:male].each {|user| puts user}
Yuji                                 
Taro              
=> ["Yuji", "Taro"]

そうすることで users[:male]Array#each で回すことなどもできるようになる。

ブロックを用いてデフォルト値を設定する

pry(main)> users = Hash.new { |h, k| h[k] = [] }                                                                                                                          
=> {}             
pry(main)> users[:male]           
=> []

Hash.new のときにブロックを渡すと、対応する値がまだ無いキーが呼び出される度に式が評価される。 したがって、上のようにまだ存在しない :male というキーで値を取ろうとしたときに配列が返ってきている。

これを使えばキーが存在する・しないにかかわらず、 Array#<< を用いて次のように値を追加していける。

pry(main)> users[:male] << "Yuji"
=> ["Yuji"]                     
pry(main)> users[:male] << "Taro"                                                                                                                                 
=> ["Yuji", "Taro"]                   
pry(main)> users[:male] << "Ryo"                                                                                                                          
=> ["Yuji", "Taro", "Ryo"]      
pry(main)> users                
=> {:male=>["Yuji", "Taro", "Ryo"]}

同様に女性も追加するなら

pry(main)> users[:female]
=> []
pry(main)> users[:female] << "Saori"
=> ["Saori"]
pry(main)> users[:female] << "Aiko"                                                                                                
=> ["Saori", "Aiko"]

こんな感じ。 ハッシュの中身を見てみると...

pry(main)> users
=> {:male=>["Yuji", "Taro", "Ryo"], :female=>["Saori", "Aiko"]}

といったように male と female で分類しながら、値に複数の名前をとるような構造ができあがっている。

女性の名前を取り出したいときは key を指定しながら each 処理するなどして次のようにかける。

pry(main)> users[:female].each { |user| puts "#{user} さんは女性です" }                                                            
Saori さんは女性です
Aiko さんは女性です
=> ["Saori", "Aiko"]

refs: