読者です 読者をやめる 読者になる 読者になる

Rubyでキーの配列と値の配列からハッシュを作る

こういう2つの配列から

keys = %w(k1 k2 k3)
vals = %w(v1 v2 v3)

こういうハッシュを作るようなときです。

{"k1"=>"v1", "k2"=>"v2", "k3"=>"v3"}

よく使われているのは1次元配列に展開してHash[]やHash.new()に渡す方法です。

a1 = keys.zip(vals)    ## [keys,vals].transposeでも可
# => [["k1", "v1"], ["k2", "v2"], ["k3", "v3"]]

a2 = a1.flatten
# => ["k1", "v1", "k2", "v2", "k3", "v3"]

hash = Hash[*a2]       ## *は配列をメソッド引数に展開する演算子
# => {"k1"=>"v1", "k2"=>"v2", "k3"=>"v3"}

もちろん書く時は一行でいいです。

hash = Hash[*keys.zip(vals).flatten]


ただこの方法ではキー、値に配列が入るとおかしなことになります。
flattenが3次元以上の配列も一気に1次元配列まで展開してしまうからです。

keys = [['k1_1', 'k1_2'], 'k2']
vals = ['v1', ['v2_1', 'v2_2']]

a1 = keys.zip(vals)
# => [[["k1_1", "k1_2"], "v1"], ["k2", ["v2_1", "v2_2"]]]
a2 = a1.flatten
# => ["k1_1", "k1_2", "v1", "k2", "v2_1", "v2_2"]
hash = Hash[*a2]
# => {"k1_1"=>"k1_2", "v1"=>"k2", "v2_1"=>"v2_2"}

これを考慮するならflattenを使うのをやめて1つずつループで代入していくしかありません。

a1 = keys.zip(vals)
hash = {}
a1.each{|k,v| hash[k] = v}
# => hash : {["k1_1", "k1_2"]=>"v1", "k2"=>["v2_1", "v2_2"]}

もちろん以下でOK

hash = {}
keys.zip(vals).each{|k,v| hash[k] = v}

けっこう煩雑。メソッド化してもよかったのではと思ってしまいます。