3

I'd like to convert from

{'key1' => (1..10) ,
 'key2' => (11..20) , 
'key3' => (21..30)}

to

[{'key1' => 1, 'key2' => 11, 'key3' => 21},
{'key1' => 1, 'key2' => 11, 'key3' => 22},...
 .
 .
{'key1' => 10, 'key2' => 20, 'key3' => 30}]

How to solve it?

2
  • Freddie, I've edited my answer to address your comment and also added an alternative, which I think is an improvement. Commented Sep 29, 2013 at 18:06
  • What have you tried, they should ask you before they unleashed themselves to answer your otherwise trivial question. Commented Oct 21, 2013 at 4:52

6 Answers 6

4

Here it is :

hsh = {'key1' => (1..10) ,'key2' => (11..20) , 'key3' => (21..30)}
keys = hsh.keys
hsh['key1'].to_a.product(hsh['key2'].to_a,hsh['key3'].to_a).map{|a|Hash[keys.zip(a)]}

# => [{'key1' => 1, 'key2' => 11, 'key3' => 21},
#   {'key1' => 1, 'key2' => 11, 'key3' => 22},...
#   .
#   .
#   {'key1' => 10, 'key2' => 20, 'key3' => 30}]

You could also write the above as below,when you have more number of keys:

hsh = {'key1' => (1..10) ,'key2' => (11..20) , 'key3' => (21..30)}
keys = hsh.keys
array = hsh.values_at(*keys[1..-1]).map(&:to_a)
hsh['key1'].to_a.product(*array).map{|a|Hash[keys.zip(a)]}
Sign up to request clarification or add additional context in comments.

2 Comments

GREAT! Solution .. but can I calc it for any size of keys?
Interesting use of splat, Arup. I borrowed it for my second answer.
2

So many ways... A kiss answer (edited to extend to any number of keys):

s = {'key1' => (1..10), 'key2' => (11..20), 'key3' => (21..30)}
r = []
s.each {|k,v| a = []; (v.to_a).each {|i| a << {k=>i}}; r << a}
result = r.shift
r.each {|e| result = result.product(e).map(&:flatten)}
result

3 Comments

GREAT! Solution! thank you so much. but can I calc it for any size of keys?
@Cary You can write I think result[0].product(result[1]).product(result[2]).flatten(1) as result[0].product(*result[1..-1]).flatten
Thanks, Arup. You spotted my mistake. I've edited the code to address SO's concern.
2
h = {
  'key1' => (1..10),
  'key2' => (11..20),
  'key3' => (21..30)
}

h.map { |k,v| [k].product(v.to_a) }.transpose.map { |e| Hash[e] }
#=> [{"key1"=>1, "key2"=>11, "key3"=>21},
#    {"key1"=>2, "key2"=>12, "key3"=>22},
#    {"key1"=>3, "key2"=>13, "key3"=>23},
#    {"key1"=>4, "key2"=>14, "key3"=>24},
#    {"key1"=>5, "key2"=>15, "key3"=>25},
#    {"key1"=>6, "key2"=>16, "key3"=>26},
#    {"key1"=>7, "key2"=>17, "key3"=>27},
#    {"key1"=>8, "key2"=>18, "key3"=>28},
#    {"key1"=>9, "key2"=>19, "key3"=>29},
#    {"key1"=>10, "key2"=>20, "key3"=>30}]

1 Comment

Interesting use of transpose, Stefan. I've tucked that into my grey cells.
1
h = {'key1' => (1..10) ,
'key2' => (11..20) , 
'key3' => (21..30)}

arrays = h.values.map(&:to_a).transpose
p arrays.map{|ar| Hash[h.keys.zip(ar)] }
#=> [{"key1"=>1, "key2"=>11, "key3"=>21}, {"key1"=>2, "key2"=>12, "key3"=>22},...

Comments

1
h = {'key1' => (1..10), 'key2' => (11..20), 'key3' => (21..30)}

Edit 1: made some changes, principally the use of inject({}):

f,*r = h.map {|k,v| [k].product(v.to_a)}
f.zip(*r).map {|e| e.inject({}) {|h,a| h[a.first] = a.last; h}}

Edit 2: After seeing the use of Hash[] in @Phrogz's answer to another question:

f,*r = h.map {|k,v| [k].product(v.to_a)}
f.zip(*r).map {|e| Hash[*e.flatten]}

Comments

0

Lazier way of doing the same:

h = {
  'key1' => (1..10),
  'key2' => (11..20),
  'key3' => (21..30)
}

result = ( 0...h.values.map( &:to_a ).map( &:size ).max ).map do |i|
  Hash.new { |hsh, k| hsh[k] = h[k].to_a[i] }
end

result[1]['key3'] #=> 22

Comments

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.