tags:

views:

549

answers:

5

We have the following datastructures:

{:a => ["val1", "val2"], :b => ["valb1", "valb2"], ...}

And I want to turn that into

[{:a => "val1", :b => "valb1"}, {:a => "val2", :b => "valb2"}, ...]

And then back into the first form. Anybody with a nice looking implementation?

A: 

You could use inject to build an array of hashes.

hash = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] }
array = hash.inject([]) do |pairs, pair|
  pairs << { pair[0] => pair[1] }
  pairs
end
array.inspect # => "[{:a=>["val1", "val2"]}, {:b=>["valb1", "valb2"]}]"

Ruby documentation has a few more examples of working with inject.

Tate Johnson
hum... can you tell me more?
Julien Genestoux
Thanks... but sorry, that didn't work with more items in the hash and arrays.
Julien Genestoux
A: 

This will work assuming all the arrays in the original hash are the same size:

hash_array = hash.first[1].map { {} }
hash.each do |key,arr|
  hash_array.zip(arr).each {|inner_hash, val| inner_hash[key] = val}
end
Chuck
+6  A: 

This solution works with arbitrary numbers of values (val1, val2...valN):

{:a => ["val1", "val2"], :b => ["valb1", "valb2"]}.inject([]){|a, (k,vs)| 
  vs.each_with_index{|v,i| (a[i] ||= {})[k] = v} 
  a
}
# => [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}]

[{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}].inject({}){|a, h| 
  h.each_pair{|k,v| (a[k] ||= []) << v}
  a
}
# => {:a=>["val1", "val2"], :b=>["valb1", "valb2"]}
Avdi
W00t! that works :) even with more items in the hash/arrays :) Good work!
Julien Genestoux
+1  A: 

Let's look closely what the data structure we are trying to convert between:

#Format A
[
 ["val1", "val2"],          :a
 ["valb1", "valb2"],        :b 
 ["valc1", "valc2"]         :c 
]
#Format B
[ :a        :b       :c
 ["val1", "valb1", "valc1"],
 ["val2", "valb2", "valc3"]
]

It is not diffculty to find Format B is the transpose of Format A in essential , then we can come up with this solution:

h={:a => ["vala1", "vala2"], :b => ["valb1", "valb2"], :c => ["valc1", "valc2"]}
sorted_keys =  h.keys.sort_by {|a,b| a.to_s <=> b.to_s}

puts sorted_keys.inject([])  {|s,e| s << h[e]}.transpose.inject([])   {|r, a| r << Hash[*sorted_keys.zip(a).flatten]}.inspect
#[{:b=>"valb1", :c=>"valc1", :a=>"vala1"}, {:b=>"valb2", :c=>"valc2", :a=>"vala2"}]
pierr
+1  A: 
m = {}
a,b = Array(h).transpose
b.transpose.map { |y| [a, y].transpose.inject(m) { |m,x| m.merge Hash[*x] }}
DigitalRoss