Ruby

歯抜け配列からハッシュの配列を作りたい

RubyでWILLCOMから取り出した電話帳データのCSVの処理を書いたのだが、ちょっと面白い処理があった。読み込んだCSVは読みとかグループとかいらない情報が多いので、電話番号とかメアドとかいる情報だけ抜き出したい。次のような何列目に必要なデータがあるかを格納したハッシュとCSVから抜き取った2次元配列から、

target = { :name => 1, :phone => 3}
csvarray = [["hoge", "", "070-1111-2222"],
             ["huga",  "", "080-2222-3333"]]

次のようなハッシュの配列に変換したい。

[{ :name=>"hoge", :phone=>"070-1111-2222"},
 { :name=>"huga", :phone=>"080-2222-3333"}]

最初に書いたのがこれ。

# pattern 1 Array#each + Hash.each
a = []
csvarray.each do |r|
  h = {}
  target.each{|k,v| h[k] = r[v-1]}
  a < < h
end
pp a

Array#mapを使ってこれを1行にするとこうなる。

# pattern 2 Array.map + Hash.each
pp csvarray.map{|r| h = {}; target.each{|k,v| h[k] = r[v-1]}; h}

で、それをinjectでやる方法を見て感動して改良したのがこれ。

# pattern 3 Array#map + Array#inject
pp csvarray.map{|r| target.keys.inject({}){|h,k| h.merge k => r[target[k]-1]}}

せっかくのハッシュなのにキーで抜いてきてまたブロック内でまたtarget[k]にアクセスしているのがちょっと残念なのでHash#mapを使ってみる。2次元配列をflattenで1次元配列に変換して*で多重代入にしてHash[]でハッシュを作成している。

# pattern 4 Array#map + Hash#map
pp csvarray.map{|r| Hash[*target.map{|k,v| [k, r[v-1]]}.flatten]}

苦しい、苦しいです。Hash#injectなんてあるのかよと思ったが、HashもEnumerableだからあるんです。ブロックパラメーターpにはkeyとvalueが配列で格納される。

# pattern 5 Array#map + Hash#inject #1
pp csvarray.map{|r| target.inject({}){|h,p| h.merge p[0] => r[p[1]-1]}}

これはpを(k, v)で受ければよさそうということでたどり着いたのがこれ。美しい!

# pattern 6 Array#map + Hash#inject #2
pp csvarray.map{|r| target.inject({}){|h,(k,v)| h.merge k => r[v-1]}}

でも、結局pattern 2が一番長さが短いのね。ちょっとがっくり。これだけでも結構遊べました。Rubyって面白いですねー。

similar posts

comment

よろしければ、コメントをどうぞ。トラックバックはこちら

このエントリーのコメントの購読

次のHTMLタグが使えます。: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

*Required Fields