tags:

views:

99

answers:

3

For my first question here I would like to ask you how you'd do the following in Ruby.

I have a hash with the following aspect

variables["foo"] = [1,2,3]
variables["bar"] = [4,5,6,7]
variables[...] = ...

Update: this hash will have an arbitrary number of key => values pairs.

So it represents parameters and their possible values. I would like to "generate" now an Array containing hashes whose key=>value pairs represent each possible combination of the variables. In the case of the example above, I would have an array of 12 (=3x4) hashes like that

[ hash1, hash2, ..., hash16]

where hash*i* would be

 hash1["foo"] = 1
 hash1["bar"] = 4
 hash2["foo"] = 1
 hash2["bar"] = 5
 hash3["foo"] = 1
 hash3["bar"] = 6
 hash4["foo"] = 1
 hash4["bar"] = 7
 hash5["foo"] = 2
 hash5["bar"] = 4
 hash6["foo"] = 3
 hash6["bar"] = 4
 ...
 hash16["foo"] = 3
 hash16["bar"] = 7

I have a few ideas but all of them are quite complicated nested loops ...

Thanks a lot !

A: 

Ruby syntax for an Array is brackets, and for a hash is curly braces with a key, hash rocket => value.

variables = { "foo" => [1,2,3] , "bar" => [4,5,6,7] }

results = Array.new

variables["foo"].each do |foo_variable|
  variables["bar"].each do |bar_variable|
    results << { "foo" => foo_variable , "bar" => bar_variable }
  end
end

require "pp"
pp results
Joshua Cheek
Thanks. But I forgot to say that the "variables" hash has an arbitrary number of entries.
Cedric H.
+1  A: 
vars = {foo: [1, 2, 3], bar: [4, 5, 6, 7]}

(v = vars.map {|k, v| ([k] * v.length).zip(v) }).first.product(*v.drop(1)).
map {|args| args.reduce({}) {|h, (k, v)| h.tap {|h| h[k] = v }}}
# => [{:foo=>1, :bar=>4},
# =>  {:foo=>1, :bar=>5},
# =>  {:foo=>1, :bar=>6},
# =>  {:foo=>1, :bar=>7},
# =>  {:foo=>2, :bar=>4},
# =>  {:foo=>2, :bar=>5},
# =>  {:foo=>2, :bar=>6},
# =>  {:foo=>2, :bar=>7},
# =>  {:foo=>3, :bar=>4},
# =>  {:foo=>3, :bar=>5},
# =>  {:foo=>3, :bar=>6},
# =>  {:foo=>3, :bar=>7}]

This works with arbitrary many entries and arbitrary keys.

Jörg W Mittag
Thanks a lot ! I still have to understand it completely but it works as I wanted.
Cedric H.
+1  A: 
vars.values.inject(&:product).map{|values|
  Hash[vars.keys.zip(values.flatten)]
}
Mladen Jablanović
This is really nice. I was toying around with something similar, but then I couldn't find anything in the language specification that would guarantee that `Hash#keys` and `Hash#values` would return the items in the same order, so I backed away from it. But now I realize that I am using some 1.9 features anyway, and in 1.9, `Hash` es are of course guaranteed to be ordered anyhow, so your solution is much nicer, assuming that you run it on 1.9.
Jörg W Mittag