将数组的哈希变成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 B
是Format 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
例子 。