リストのリストから,ハッシュのリストを作る

「怠慢はプログラマの美徳」というけれど - kなんとかの日記 を見て,

例えば、スクリプト言語で次のような Hash や Dict を書いたとする。

data = [
  {'name'=>'Foo', 'age'=>20, 'email'=>'foo@mail.com'},
  {'name'=>'Bar', 'age'=>21, 'email'=>'bar@mail.net'},
  {'name'=>'Baz', 'age'=>22, 'email'=>'baz@mail.org'},
]

もしこれをみて何も感じないのであれば、スクリプト言語屋といえど Java 屋を笑うことはできない。本質的でない記述に嫌悪感を感じるセンスがあれば、同じキーが何度も現れていることを「めんどくさい」と感じるはずだ。そして、こんなふうに書けないだろうかと一度は思うはずだ。

data = %h{
  ['name', 'age', 'email'],
  ['Foo',   20,   'foo@mail.com'],
  ['Bar',   21,   'bar@mail.net'],
  ['Baz',   22,   'baz@mail.org'],
}
「怠慢はプログラマの美徳」というけれど - kなんとかの日記

何だかむずむずしてきたので,リストのリストからハッシュのリストを生成するコード片を書いてみた.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH
    lambda {|keys, columns|
      columns.map! {|column| Hash[*keys.zip(column).flatten] }
    }.call(self.shift, self)
  end
end

table = [
  [:name, :age, :email],
  ['Foo',   20, 'foo@mail.com'],
  ['Bar',   21, 'bar@mail.net'],
  ['Baz',   22, 'baz@mail.org'],
]

pp table.to_AoH

実行結果はこんな感じ.

% ./table2arrayOfHash.rb
[{:email=>"foo@mail.com", :name=>"Foo", :age=>20},
 {:email=>"bar@mail.net", :name=>"Bar", :age=>21},
 {:email=>"baz@mail.org", :name=>"Baz", :age=>22}]

でも,こっちの方が好みかなぁ.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH(*keys)
    map {|column| Hash[*keys.zip(column).flatten] }
  end
end

aoh = [
  ['Foo', 20, 'foo@mail.com'],
  ['Bar', 21, 'bar@mail.net'],
  ['Baz', 22, 'baz@mail.org'],
].to_AoH(:name, :age, :email)

pp aoh

いや,やはりキーはまず値より先に現れるべきか,と思い直してさらに修正.

#!/usr/local/bin/ruby
require 'pp'

class Array
  def to_AoH(*lists)
    lists.map {|values| Hash[*self.zip(values).flatten]}
  end
end

aoh = [:name, :age, :email].to_AoH(
  ['Foo', 20, 'foo@mail.com'],
  ['Bar', 21, 'bar@mail.net'],
  ['Baz', 22, 'baz@mail.org']
)

pp aoh

各リストをそれぞれメソッドの引数に積んでいるので,最後の要素の後ろにカンマ(',')を書けないところが若干気持ち悪いけど,こんなところかなぁ.