Sum ruby​​哈希值

Jor*_*anC 5 ruby hash reduce inject

我试图从ruby散列中求和值,但使用inject或reduce不会返回正确的答案.看起来这些方法似乎覆盖了存储的当前值而不是对它们求和.

我的哈希看起来像这样:

@test = [
  {"total"=>18, "type"=>"buy", "date"=>Thu, 21 Nov 2013, "instrument_code"=>"food"},
  {"total"=>92, "type"=>"buy", "date"=>Thu, 14 Nov 2013, "instrument_code"=>"food"},
  {"total"=>12, "type"=>"buy", "date"=>Wed, 20 Nov 2013, "instrument_code"=>"drink"},
  {"total"=>1, "type"=>"buy", "date"=>Mon, 11 Nov 2013, "instrument_code"=>"food"}
]
Run Code Online (Sandbox Code Playgroud)

这是我的注入代码失败:

def additions_per_security
  @test.group_by { |i| i.type }.each do |key, value|
    if key == "buy"
      value.group_by{ |i| i.date }.each do |key, value|
        @sortable_additions[key] = value
      end
      @sorted_additions = @sortable_additions.sort_by { |key,value| key }
      @sorted_additions.shift
      @additions_per_security = Hash[@sorted_additions.map { |key, value| 
       [key, value]
      }]
      @additions_per_security.each do |key, value|
        value.group_by { |i| i.instrument_code }.each do |key, value|
          @additions_security[key] = value.inject(0){ |result, transaction| 
            (result += transaction.total)
          }
        end
      end
    end
  end
  return @additions_security
end
Run Code Online (Sandbox Code Playgroud)

这是我的reduce代码失败:

def additions_per_security
  @@test.group_by { |i| i.type }.each do |key, value|
    if key == "buy"
      value.group_by { |i| i.date }.each do |key, value|
        @sortable_additions[key] = value
      end
      @sorted_additions = @sortable_additions.sort_by { |key,value| key }
      @sorted_additions.shift
      @additions_per_security = Hash[@sorted_additions.map { |key, value| 
        [key, value]
      }]
      @additions_per_security.each do |key, value|
        value.group_by { |i| i.instrument_code }.each do |key, value|
          @additions_security[key] = value.map { |p| p.total }.reduce(0,:+)
        end
      end
    end
  end
  return @additions_security
end
Run Code Online (Sandbox Code Playgroud)

我有一个哈希,我想要除了第一个日期之外的所有键的总和.

我目前正在收到以下信息:

{"drink"=>12.0, "food"=>92}
Run Code Online (Sandbox Code Playgroud)

我的预期结果将如下所示:

{"drink"=>12.0, "food"=>110}
Run Code Online (Sandbox Code Playgroud)

提前感谢任何建议.

m4t*_*m4t 26

如果你有简单的键/值哈希

{1 => 42, 2 => 42}.values.sum
 => 84 
Run Code Online (Sandbox Code Playgroud)

  • `{1 => 42,2 => 30} .values.inject(:+)` (3认同)

Car*_*and 7

我对您的inject代码提供以下观察:

  • 没有变量需要是实例变量; 局部变量(否@)就足够了;
  • test.group_by {|i| i.type}... 应该 test.group_by {|i| i["type"]}...
  • @sortable_additions[key]=value 应该引发异常,因为尚未创建哈希;
  • @sorted_additions.shift删除哈希的第一个元素并返回该元素,但没有变量来接收它(例如,, h = @sorted_additions.shift);
  • @additions_per_security = Hash[@sorted_additions.map { |key, value|[key, value]}] 似乎将@sorted_additions转换为数组,然后返回到相同的哈希.

以下是一种做你想做的事情的方法.

首先,您将传递日期对象.为了解决这个问题,我们首先为您示例中的日期创建日期对象:

require 'date'
date1 = Date.parse("Thu, 21 Nov 2013") # => #<Date: 2013-11-21 ((2456618j,0s,0n),+0s,2299161j)>
date2 = Date.parse("Thu, 14 Nov 2013") # => #<Date: 2013-11-14 ((2456611j,0s,0n),+0s,2299161j)>
date3 = Date.parse("Thu, 20 Nov 2013") # => #<Date: 2013-11-20 ((2456617j,0s,0n),+0s,2299161j)>
date4 = Date.parse("Thu, 11 Nov 2013") # => #<Date: 2013-11-11 ((2456608j,0s,0n),+0s,2299161j)>
Run Code Online (Sandbox Code Playgroud)

用于检测:

test = [{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
        {"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"},
        {"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"},
        {"total"=> 1, "type"=>"buy", "date"=>date4, "instrument_code"=>"food"}]
Run Code Online (Sandbox Code Playgroud)

现在我们计算我们需要的东西.

test_buy = test.select {|h| h["type"] == "buy"}

earliest = test_buy.min_by {|h| h["date"]}["date"]
  # => #<Date: 2013-11-11 ((2456608j,0s,0n),+0s,2299161j)>

all_but_last = test.reject {|h| h["date"] == earliest}
 # =>  [{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
        {"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"},
        {"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"}] 
Run Code Online (Sandbox Code Playgroud)

或者我们可以使用Enumerable#select:

all_but_last = test.select {|h| h["date"] != earliest}
Run Code Online (Sandbox Code Playgroud)

注意,这里和下面的值date1,date2date3会显示(例如,#<Date: 2013-11-21 ((2456618j,0s,0n),+0s,2299161j)>将显示date1); 我在这里使用变量名作为占位符,以使其更具可读性.此外,所有哈希值hh["date"] = earliest将被拒绝(应该有不止一个).

grouped = all_but_last.group_by {|h| h["instrument_code"]}
 # => {"food" =>[{"total"=>18, "type"=>"buy", "date"=>date1, "instrument_code"=>"food"},
                  {"total"=>92, "type"=>"buy", "date"=>date2, "instrument_code"=>"food"}],
       "drink"=>[{"total"=>12, "type"=>"buy", "date"=>date3, "instrument_code"=>"drink"}]}

keys = grouped.keys # => ["food", "drink"]

arr = keys.map {|k| [k, grouped[k].reduce(0) {|t,h| t + h["total"]}]}
  # => [["food", 110], ["drink", 12]]

Hash[arr] # => {"food"=>110, "drink"=>12} 
Run Code Online (Sandbox Code Playgroud)

我使用了一些临时变量,包括test_buy,earliest,all_but_last,grouped,keysarr.您可以通过"链接"来消除其中的一些.在这里,我将向您展示如何摆脱其中一些:

test_buy = test.select {|h| h["type"] == "buy"}
earliest = test_buy.min_by {|h| h["date"]}["date"]
grouped = test_buy.reject {|h| h["date"] == earliest}.group_by \
  {|h| h["instrument_code"]}
Hash[grouped.keys.map {|k| [k, grouped[k].reduce(0) \
  {|t,h| t + h["total"]}]}] # => {"food"=>110, "drink"=>12}
Run Code Online (Sandbox Code Playgroud)

您可能认为这看起来很复杂,但在您获得Ruby经验后,它看起来非常自然且易于阅读.但是,使用链接的程度是样式首选项.