将数组的哈希变成Ruby中的哈希数组

我们有以下数据结构:

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

我想把它变成

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

然后回到第一个表格。 任何看起来很漂亮的人?

此解决方案适用于任意数量的值(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"]} 

使用函数方法(参见Enumerable):

 hs = h.values.transpose.map { |vs| h.keys.zip(vs).to_h } #=> [{:a=>"val1", :b=>"valb1"}, {:a=>"val2", :b=>"valb2"}] 

然后回来:

 h_again = hs.first.keys.zip(hs.map(&:values).transpose).to_h #=> {:a=>["val1", "val2"], :b=>["valb1", "valb2"]} 

让我们仔细看看我们试图在哪些数据结构之间进行转换:

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

找到Format BFormat A的转置是非常困难的,那么我们可以提出这个解决方案:

 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"}] 
 m = {} a,b = Array(h).transpose b.transpose.map { |y| [a, y].transpose.inject(m) { |m,x| m.merge Hash[*x] }} 

我的尝试,也许稍微紧凑一点。

 h = { :a => ["val1", "val2"], :b => ["valb1", "valb2"] } h.values.transpose.map { |s| Hash[h.keys.zip(s)] } 

应该在Ruby 1.9.3或更高版本中工作。


说明:

首先,将相应的值“组合”为“行”

 h.values.transpose # => [["val1", "valb1"], ["val2", "valb2"]] 

地图块中的每次迭代都将产生以下其中一种:

 h.keys.zip(s) # => [[:a, "val1"], [:b, "valb1"]] 

Hash[]将它们变成哈希:

 Hash[h.keys.zip(s)] # => {:a=>"val1", :b=>"valb1"} (for each iteration) 

假设原始哈希中的所有数组大小相同,这将起作用:

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

您可以使用inject来构建哈希数组。

 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文档还有一些使用inject 例子 。