【问题标题】:Turning a Hash of Arrays into an Array of Hashes in Ruby在 Ruby 中将数组哈希转换为哈希数组
【发布时间】:2010-12-11 02:10:01
【问题描述】:

我们有以下数据结构:

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

我想把它变成

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

然后回到第一种形式。有漂亮的实现吗?

【问题讨论】:

  • 你的意思是{:a => ["val1", "val2", ...], :b => ["valb1", "valb2", ...], ...}??这样输出将包括,比如说,:c => "valc1", blah blah blah?
  • 是的,我们也可以有 :c => ["valc1", "valc2"] 然后输出有第一个对象有 :c => "valc1" 和 :c => “valc2”代表第二个对象...此外,每个数组可能有不止 2 个元素。
  • 对于笛卡尔积——所有可能组合的结果——见Calculate all variations for Hash of arrays

标签: ruby arrays hash


【解决方案1】:

使用函数式方法(参见 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"]}

【讨论】:

    【解决方案2】:

    我的尝试,可能稍微紧凑一些。

    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"]] 
    

    map 块中的每次迭代都会产生以下之一:

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

    Hash[] 会将它们变成哈希:

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

    【讨论】:

      【解决方案3】:
      m = {}
      a,b = Array(h).transpose
      b.transpose.map { |y| [a, y].transpose.inject(m) { |m,x| m.merge Hash[*x] }}
      

      【讨论】:

        【解决方案4】:

        让我们仔细看看我们要转换的数据结构是什么:

        #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在 essential 中的转置,那么我们可以想出这个解决方案:

        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"}]
        

        【讨论】:

          【解决方案5】:

          您可以使用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 文档中有一个 few more examplesinject 一起工作。

          【讨论】:

          • 谢谢...但很抱歉,这不适用于散列和数组中的更多项目。
          【解决方案6】:

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

          【讨论】:

          • 哇!有效:) 即使哈希/数组中有更多项目:) 干得好!
          【解决方案7】:

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-10-29
            • 2019-10-27
            • 2019-05-23
            • 2017-07-06
            • 2021-01-17
            • 1970-01-01
            相关资源
            最近更新 更多