Arrayに対するmapっぽくHashをすっきりと返す

一行くらいですっきりとHashを返すようにしたいね、という話。

  • Arrayの各要素に何かをして返したい時


ret = [1, 2, 3].map |num|
     num * 3
end

p ret
#=> [3, 6, 9]


- Arrayの各要素を元にしてHashを要素にしたArrayを返したい時

ret = [1, 2, 3].map |num|
     k = num.to_s
     {k: num}
end

p ret
#=> [{:k=>1}, {:k=>2}, {:k=>3}]


本題

  • で、何となくここまで書いたけど、Arrayを返すのはRubyでは簡単。ブロックもあるし。
  • しかしながら、どうやらRubyでmapとかfilterみたいな関数でハッシュを返すのは無いっぽい
  • eachを使って愚直にやる、というパターンもあるけど微妙
  • ちなみにPythonはリスト内包表記を使えば楽に書ける
l = range(0, 5)
{ str(elm): elm for elm in l }
#=>{'0': 0, '1': 1,  '2': 2, '3': 3, '4': 4, '5': 5}


  • ちょいっと調べて先輩に聞いてみると、以下の答えを教えてくれた。
#ハッシュの各要素に対して何かしらやって、新たにハッシュを生成する
{ a:1, b:2, c:3 }.inject( {} ) do  |h, (key, value)|  
       h[key] = value 
       h 
end
#=>{:b=>100, :a=>200, :c=>300}

#配列の各要素に対して(ry
[1, 2, 3].inject( {} ) do |h, elm|
         h[elm.to_s] = elm
         h
end

#=>{"1"=>1, "2"=>2, "3"=>3}

#無論このように一行でも書ける
[1, 2, 3].inject( {} ) do {|h, elm| h[elm.to_s] = elm; h}


  • injectのブロックの仮引数である、hがinjectの仮引数である{}と対応している。

  • したがって以下の二つは同じことになる

#パターン1
[1, 2, 3].inject({}) do |h, elm|#仮引数が{}
#略
end
#パターン2
[1, 2, 3].inject(Hash.new) do |h, elm|#仮引数がHash.new
#略
end
  • 以下の解説が詳しい。何となくこのこれでは雰囲気をつかめればいいかな、と思う


参考資料

Ruby - Hash#map は Hash を返さない - Qiita [キータ]
http://qiita.com/A-gen/items/4997dbfa7d844b72cd58

ruby の inject をわかりやすく説明してみる
http://d.hatena.ne.jp/kenkitii/20090114/