パラメータの組み合わせを作りたい時にはArray#productを使う

Cassandraのパラメータを色々変えながらデータを取りたいと思った.
が,それ以前にパラメータの組み合わせを作るだけで四苦八苦してしまったので,ここに書いておこうと思う.

Array#product

RubyにはArray#productが用意されていて,Arrayでパラメータ達を扱うのであれば全く問題ない.

[1, 2].product([3, 4])
=> [[1, 3], [1, 4], [2, 3], [2, 4]]

※ただし,Ruby 1.8.7以上で.

Hashでproduct

でも,普通はArrayで延々パラメータを連ねたりしない.ここはHashを使いたい.
しかし,Hashを使おうにもproductはなかった.仕方ないので自分で書くことにした.

# 組み合わせをHashのArrayにして返す
class Hash
  if method_defined?(:product)
    warn "WARNING: Hash#product conflict occurred."
  else
    def product
      keys = self.keys.sort { |x, y| x.to_i <=> y.to_i }
      self[keys[0]].product(*self.values_at(*keys[1..-1])).map { |*values|
        alist = keys.zip(*values)
        Hash[*alist.flatten]
      }
    end
  end
end

試してみる

例えば,パラメータaをa1 a2 a3で,パラメータbをb1 b2 b3で,パラメータcをc1 c2 c3で,それぞれの組み合わせがほしい場合.

require 'pp'

params = {
  :a => %w/a1 a2 a3/,
  :b => %w/b1 b2 b3/,
  :c => %w/c1 c2 c3/
}

pp params.product
=> [{:c=>"c1", :a=>"a1", :b=>"b1"},
 {:c=>"c2", :a=>"a1", :b=>"b1"},
 {:c=>"c3", :a=>"a1", :b=>"b1"},
 {:c=>"c1", :a=>"a1", :b=>"b2"},
 {:c=>"c2", :a=>"a1", :b=>"b2"},
 {:c=>"c3", :a=>"a1", :b=>"b2"},
 {:c=>"c1", :a=>"a1", :b=>"b3"},
 {:c=>"c2", :a=>"a1", :b=>"b3"},
 {:c=>"c3", :a=>"a1", :b=>"b3"},
 {:c=>"c1", :a=>"a2", :b=>"b1"},
 {:c=>"c2", :a=>"a2", :b=>"b1"},
 {:c=>"c3", :a=>"a2", :b=>"b1"},
 {:c=>"c1", :a=>"a2", :b=>"b2"},
 {:c=>"c2", :a=>"a2", :b=>"b2"},
 {:c=>"c3", :a=>"a2", :b=>"b2"},
 {:c=>"c1", :a=>"a2", :b=>"b3"},
 {:c=>"c2", :a=>"a2", :b=>"b3"},
 {:c=>"c3", :a=>"a2", :b=>"b3"},
 {:c=>"c1", :a=>"a3", :b=>"b1"},
 {:c=>"c2", :a=>"a3", :b=>"b1"},
 {:c=>"c3", :a=>"a3", :b=>"b1"},
 {:c=>"c1", :a=>"a3", :b=>"b2"},
 {:c=>"c2", :a=>"a3", :b=>"b2"},
 {:c=>"c3", :a=>"a3", :b=>"b2"},
 {:c=>"c1", :a=>"a3", :b=>"b3"},
 {:c=>"c2", :a=>"a3", :b=>"b3"},
 {:c=>"c3", :a=>"a3", :b=>"b3"}]