Skip to content

Instantly share code, notes, and snippets.

@mjy
Forked from maxim/variants_generator.rb
Created March 9, 2010 05:43
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mjy/326277 to your computer and use it in GitHub Desktop.
Save mjy/326277 to your computer and use it in GitHub Desktop.
require 'test/unit'
#require 'ruby-debug'
##
# Variant generator returns all possible combinations of elements of all given arrays.
# Running this gist will run the tests. For functionality only this module is needed.
module VariantsGenerator
extend self
##
# Operates on arrays.
#
# Example:
# generate_variants(["red", "blue"], ["long", "short"])
# # => [["red", "long"], ["red", "short"], ["blue", "long"], ["blue", "short"]]
#
# this is ugly but it does test clean ;)
# very hackish, but tests clean, and it doesn't require the recursion, trick is the .product bit
def generate_variants(*arrays)
t = arrays.size
# deal with all the exceptions
return nil if t == 0
if arrays.first.class == String
return arrays.first if t == 1
return [arrays]
end
# just arrays at this point
if t == 1
arrays.first
else
result = arrays.shift
arrays.each do |a|
result = result.product(a)
end
if t == 2
return result.inject([]){|sum, a| sum << a}
else
if arrays.first.first.class == Array # seems to be an odd requirement in the test ...
result.collect{|a| a.flatten.inject([]){|ar, a| ar << [a]}}
else
result.collect{|a| a.flatten}
end
end
end
end
##
# Operates on hashes where key is name and value is array.
#
# Example:
# generate_named_variants(:color => ["red", "blue"], :type => ["long", "short"])
# # => [{:color => "red", :type => "long"}, {:color => "red", :type => "short"},
# # {:color => "blue", :type => "long"}, {:color => "red", :type => "short"}]
def generate_named_variants(hash)
names, arrays = hash.to_a.transpose
generate_variants(*arrays).map { |values| Hash[names.zip(values)] }
end
end
# Tests
class Test::Unit::TestCase
include VariantsGenerator
def assert_same_elements(a1, a2, msg = nil)
a1h = a1.inject({}) { |h,e| h[e] = a1.select { |i| i == e }.size; h }
a2h = a2.inject({}) { |h,e| h[e] = a2.select { |i| i == e }.size; h }
assert_equal(a1h, a2h, msg)
end
end
class GenerateVariantsTest < Test::Unit::TestCase
def test_valid_permutations_for_2_arrays
assert_same_elements [["white", "m"],
["white", "f"],
["black", "m"],
["black", "f"]], generate_variants(["white", "black"], ["m", "f"])
end
def test_1_permutation_for_non_arrays
assert_same_elements [["foo", "bar"]], generate_variants("foo", "bar")
end
def test_array_elements_are_preserved_unflattened_for_2_arrays
assert_same_elements [[["white"], ["m"]],
[["white"], ["f"]],
[["black"], ["m"]],
[["black"], ["f"]]], generate_variants([["white"], ["black"]], [["m"], ["f"]])
end
def test_array_elements_are_preserved_unflattened_for_3_arrays
assert_same_elements [[["white"], ["m"], ["s"]],
[["white"], ["m"], ["m"]],
[["white"], ["m"], ["l"]],
[["white"], ["f"], ["s"]],
[["white"], ["f"], ["m"]],
[["white"], ["f"], ["l"]],
[["black"], ["m"], ["s"]],
[["black"], ["m"], ["m"]],
[["black"], ["m"], ["l"]],
[["black"], ["f"], ["s"]],
[["black"], ["f"], ["m"]],
[["black"], ["f"], ["l"]]], generate_variants([["white"], ["black"]],
[["m"], ["f"]],
[["s"], ["m"], ["l"]])
end
def test_valid_permutations_for_3_arrays
assert_same_elements [["s", "white", "m"],
["s", "white", "f"],
["s", "black", "m"],
["s", "black", "f"],
["m", "white", "m"],
["m", "white", "f"],
["m", "black", "m"],
["m", "black", "f"],
["l", "white", "m"],
["l", "white", "f"],
["l", "black", "m"],
["l", "black", "f"]], generate_variants(["s", "m", "l"], ["white", "black"], ["m", "f"])
end
def test_1_permutation_for_1_array_element
assert_equal ["s", "m", "l"], generate_variants(["s", "m", "l"])
end
def test_returns_single_supplied_argument
assert_equal "s", generate_variants("s")
end
def test_return_nil_with_no_arguments
assert_nil generate_variants
end
end
class GenerateNamedVariantsTest < Test::Unit::TestCase
def test_permutes_3_named_arrays
assert_same_elements [{:color=>"white", :gender=>"m", :size=>"s"},
{:color=>"white", :gender=>"m", :size=>"m"},
{:color=>"white", :gender=>"m", :size=>"l"},
{:color=>"white", :gender=>"f", :size=>"s"},
{:color=>"white", :gender=>"f", :size=>"m"},
{:color=>"white", :gender=>"f", :size=>"l"},
{:color=>"black", :gender=>"m", :size=>"s"},
{:color=>"black", :gender=>"m", :size=>"m"},
{:color=>"black", :gender=>"m", :size=>"l"},
{:color=>"black", :gender=>"f", :size=>"s"},
{:color=>"black", :gender=>"f", :size=>"m"},
{:color=>"black", :gender=>"f", :size=>"l"}],
generate_named_variants( :size => ["s", "m", "l"],
:color => ["white", "black"],
:gender => ["m", "f"])
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment