組み合わせ生成のgemを作った

先日のミートアップで発表した内容をあらためてまとめます。

hanahirodev.hatenablog.com


コンセプト

pictPairwiserを使って組み合わせテストのパターンを作るのは楽になったが、rspecで使うためには加工が必要なので、組み合わせたいパラメーターを渡したら配列にして返ってきて欲しい。

No. first_name last_name
1 “aaa” “bbb”
2 “あああ” “”
: : :

みたいなやつを

 where(:no, :first_name, :last_name) do
          [
            [1, "aaa", "bbb"],
            [2, "test", "bbb"],
            [3, "あああ", ""],
          ]
        end

のようにイイ感じに整形しないといけない。

※繰り返しのテストには、以下のgemを利用しています。 github.com

そこで、senju_kan_nonというgemを作って公開しました!!! (yamlのパーサーを書けばよかったのではないか。)


Why senju_kan_non?

gemの名前を考えていた時に、パラメーターを衆生に見立てて、テストケースによって救われるみたいなことを考えてこんな名前になりました。

千手観音 - Wikipedia

その結果、メソッド名やパラメーター名に合掌 🙏 とか、利益とか、一切衆生とか使ってます。 正直読みづらい。


アルゴリズムの紹介

高校数学をちょっと思い出す。

異なる n 個のものから r 個を選ぶ場合の、組み合わせの数

{ \displaystyle
 nCr = \frac{nPr}{r!} = \frac{n!}{r!(n-r)!}
}

Rubyの便利メソッドをつかう

combination

[1,2,3,4].combination(2) do |first, second|
  p "(#{first, #{second}})"
end

=> (1,2) (1,3) (1,4) (2,3) (2,4) (3,4)

これで、2引数のarrayを組み合わせるのが実現できる。

{
  :first=>[1, 1, 1, 2, 2, 2, 3, 3, 3], 
  :second=>["a", "b", "c", "a", "b", "c", "a", "b", "c"]
}

テーブルにすると、こんな感じ。

key v1 v2 v3 ・・・
first 1 1 1 ・・・
second a b c ・・・

目指すのはこの形なので、

No. first_name last_name
1 “aaa” “bbb”
2 “あああ” “”
: : :

行列を入れ替えたい。

Rubyの便利メソッドをつかう2

transpose

values =  { :first=>[1, 1, 1, 2, 2, 2, 3, 3, 3], 
            :second=>["a", "b", "c", "a", "b", "c", "a", "b", "c"]}
values.transpose

=> [[1, "a"], [1, "b"], [1, "c"], [2, "a"], [2, "b"], [2, "c"], [3, "a"], [3, "b"], [3, "c"]]

以上をベースに、重複したパターンの削除、テーブル作成時に欠損するデータの補完処理を足した。

これから作ろうと思っているところ

  • 網羅する水準の指定ができるようにしたい。
  • 組み合わせを作るアルゴリズムを高速に。
  • 禁則処理
  • 出力された組み合わせをファイル出力

発表した時は忘れてましたが、すでに便利なgemがあるので、そっちを使ってもいいと思います。

pairwise | RubyGems.org | your community gem host