【问题标题】:Ruby aggregate selective values within a collection of hashesRuby 在哈希集合中聚合选择性值
【发布时间】:2017-12-06 20:12:17
【问题描述】:

我有一个哈希数组,键是国家,值是天数。

我想汇总哈希值并对相同国家/地区的值求和。

数组可能看起来像这样countries = [{"Country"=>"Brazil", "Duration"=>731/1 days}, {"Country"=>"Brazil", "Duration"=>365/1 days}]

我希望它返回以下内容:[{"Country" => "Brazil", "Duration"=>1096/1 days}]

我在 SO like this one 上尝试了其他问题

countries.inject{|new_h, old_h| new_h.merge(old_h) {|_, old_v, new_v| old_v + new_v}}

产生{"Country"=>"BrazilBrazil", "Duration"=>1096/1 days}

有没有办法选择性地只合并特定值?

【问题讨论】:

    标签: ruby-on-rails ruby hash


    【解决方案1】:

    这使用Hash::new 的形式创建一个具有默认值的空散列(此处为0)。对于以这种方式创建的散列h,如果散列没有键kh[k] 将返回默认值。哈希没有被修改。

    countries = [{"Country"=>"Brazil",    "Duration"=>"731/1 days"},
                 {"Country"=>"Argentina", "Duration"=>"123/1 days"},
                 {"Country"=>"Brazil",    "Duration"=>"240/1 days"},
                 {"Country"=>"Argentina", "Duration"=>"260/1 days"}]
    
    countries.each_with_object(Hash.new(0)) {|g,h| h[g["Country"]] += g["Duration"].to_i }.
      map { |k,v| { "Country"=>k, "Duration"=>"#{v}/1 days" } }
        #=> [{"Country"=>"Brazil",    "Duration"=>"971/1 days"},
        #    {"Country"=>"Argentina", "Duration"=>"383/1 days"}]
    

    第一个哈希传递给区块并分配给区块变量g

    g = {"Country"=>"Brazil", "Duration"=>"731/1 days"}
    

    此时h #=> {}。然后我们计算

    h[g["Country"]] += g["Duration"].to_i
      #=> h["Brazil"] += "971/1 days".to_i
      #=> h["Brazil"] = h["Brazil"] + 971
      #=> h["Brazil"] = 0 + 971 # h["Brazil"]
    

    请参阅 String#to_i 了解为什么 "971/1 days".to_i 返回 971

    等式右边的h["Brazil"] 返回默认值0,因为h(还)没有键"Brazil"。请注意,右侧的h["Brazil"]h.[]("Brazil") 的语法糖,而左侧是h.[]=(h["Brazil"] + 97) 的语法糖。当哈希没有给定键时,Hash#[] 返回默认值。其余步骤类似。

    【讨论】:

    • Hash.new(0) 会将持续时间转换为秒。您可能想尝试做Hash.new(0.days),然后在地图块内您可以做"Duration" => v
    • @TheLegend,感谢您的建议。我不了解 Rails,所以我提供了一个纯 Ruby 解决方案。
    【解决方案2】:

    您可以按如下方式更新您的代码:

    countries.inject do |new_h, old_h| 
        new_h.merge(old_h) do |k, old_v, new_v|
            if k=="Country" then old_v else old_v + new_v end
        end 
    end
    #  => {"Country"=>"Brazil", "Duration"=>1096} 
    

    您基本上使用k (for key) 参数在不同的合并策略之间切换。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-14
      • 2014-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-15
      • 1970-01-01
      • 2011-07-31
      相关资源
      最近更新 更多