0

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 hashi 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 !

1
  • I would like to first have the first value of the first variable and every values of the second one, then the second value of the first variable and every values of the second one, etc. Commented Aug 16, 2010 at 12:27

3 Answers 3

1
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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a lot ! I still have to understand it completely but it works as I wanted.
1

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

1 Comment

Thanks. But I forgot to say that the "variables" hash has an arbitrary number of entries.
1
vars.values.inject(&:product).map{|values|
  Hash[vars.keys.zip(values.flatten)]
}

1 Comment

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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.