HashでArray#map!(collect!)のようなことを実現する

意外とハマったのでメモ。


例えば、配列の各要素を数値から文字列に変換したいと思ったら、map!(collect!)メソッドを使って実現できます。

array = [1, 2]

array.map!{|item| item.to_s}
#=> ["1", "2"]


では、これと同様のことをハッシュで行う(各キーに割り当てられた値を数値から文字列に変換する)には、どうすればよいでしょうか。
HashクラスもEnumerableインクルードしてるから、map!で実現できる?

hash = {:a => 1, :b => 2}

hash.map!{|key, value| value.to_s}
#=> NoMethodError: undefined method `map!' for {:a=>1, :b=>2}:Hash

NoMethodErrorとなってしまいました。
Rubyリファレンスマニュアルにあるとおり、mapは「配列を返」すため、Hashオブジェクトに対する破壊的メソッドとしては機能しないのですね。
また、私は「map = 置換メソッド」という認識をしていたために上記のような書き方をしましたが、この認識もあまりよくないと思いました。
mapは「ブロックを評価した結果を全て含む配列を返」すので、この書き方では、value.to_s の集合を期待していることになってしまいます。


ハッシュは hash[key] = value の形で値を指定できるので、ハッシュのまま値を変換するなら、次のような方法があります。

hash.each{|key, value| hash[key] = value.to_s} 
#=> {:a=>"1", :b=>"2"}


あるいは、mapで得た配列からハッシュを生成しなおす方法もあります。

alist = hash.map{|key, value| [key, value.to_s]}
#=> [[:a, "1"], [:b, "2"]]
Hash[*alist.flatten]
#=> {:a=>"1", :b=>"2"}


個人的には、一行で書ける前者が好みですね。
もっとスマートな方法があればご教示下さい。