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って面白いですねー。
よろしければ、コメントをどうぞ。トラックバックはこちら
このエントリーのコメントの購読
次の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="">