1

基本上我有一个像这样的哈希数组:

[
  { :id => 20, :total => 1, :total2 => 0 },
  { :id => 21, :total => 1, :total2 => 0 },
  { :id => 22, :total => 2, :total2 => 0 },
  { :id => 23, :total => 1, :total2 => 0 },
  { :id => 20, :total => 1, :total2 => 0 },
  { :id => 21, :total => 1, :total2 => 0 },
  { :id => 22, :total => 1, :total2 => 1 },
  { :id => 23, :total => 1, :total2 => 0 }
]

我希望数组像这样对最后两个哈希列求和,保持第一个 ( :id) 作为标识符:

[
  { :id => 20, :total => 2, :total2 => 0 },
  { :id  => 21, :total => 2, :total2 => 0 },
  { :id  => 22, :total => 3, :total2 => 1 }
]

我环顾四周,似乎.inject()在这种情况下使用了该方法,但我无法真正弄清楚语法/如何使用它。

我正在寻找的是将第一列(:id)保留为 ID 字段;如果有另一个具有此 ID 的哈希,就像我上面的示例一样,则应将两个哈希相加。

4

5 回答 5

1

你能试试这个吗?

array = [{:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0},{:stemp=>22, :vtotal=>2, :avg=>0}, {:stemp=>23, :vtotal=>1, :avg=>0},  {:stemp=>20, :vtotal=>1, :avg=>0}, {:stemp=>21, :vtotal=>1, :avg=>0}, {:stemp=>22, :vtotal=>1, :avg=>1}, {:stemp=>23, :vtotal=>1, :avg=>0}]

result = array.group_by{|h| h[:stemp]}.map do |stemp, hashes|
  { stemp: stemp, vtotal: hashes.map{|h| h[:vtotal]}.inject(:+), avg: hashes.map{|h| h[:avg]}.inject(:+) }
end

只需使用 ruby​​ 1.9.3 将其复制粘贴到 IRB 控制台中,输出如下:

[
  {:stemp=>20, :vtotal=>2, :avg=>0},
  {:stemp=>21, :vtotal=>2, :avg=>0},
  {:stemp=>22, :vtotal=>3, :avg=>1},
  {:stemp=>23, :vtotal=>2, :avg=>0}
] 
于 2013-08-22T15:52:03.403 回答
0

我重新格式化了问题和答案中的数据,以便其他人更容易看到发生了什么。

data = [
  { :stemp => 20, :vtotal => 1, :avg => 0 },
  { :stemp => 21, :vtotal => 1, :avg => 0 },
  { :stemp => 22, :vtotal => 2, :avg => 0 },
  { :stemp => 23, :vtotal => 1, :avg => 0 },
  { :stemp => 20, :vtotal => 1, :avg => 0 },
  { :stemp => 21, :vtotal => 1, :avg => 0 },
  { :stemp => 22, :vtotal => 1, :avg => 1 },
  { :stemp => 23, :vtotal => 1, :avg => 0 }
]

首先,将您的哈希按stemp.

data = data.group_by { |datum| datum[:stemp] }

然后遍历每个stemp及其条目。

data = data.map do |stemp, entries|
  # this pulls out each hash's :vtotal entry and then combines it with the + operator
  vtotal = entries.map { |entry| entry[:vtotal] }.inject(&:+)
  # this does the same as above, but for the avg entry
  avg = entries.map { |entry| entry[:avg] }.inject(&:+)
  { :stemp => stemp, :vtotal => vtotal, :avg => avg }
end

这输出

[
  { :stemp => 20, :vtotal => 2, :avg => 0 },
  { :stemp => 21, :vtotal => 2, :avg => 0 },
  { :stemp => 22, :vtotal => 3, :avg => 1 },
  { :stemp => 23, :vtotal => 2, :avg => 0 }
]
于 2013-08-22T15:55:53.017 回答
0

该解决方案具有可读性,但我想提供它以供参考。

Hash#merge接受一个在找到冲突键时将执行的块。

arr = [ {:id => 20, :total => 1, :total2 => 0} ... ]

arr.group_by{ |h| h[:id] }.map do |_, hash|
  hash.reduce do |hash_a, hash_b|
    hash_a.merge(hash_b){ |key, old, new| key == :id ? old : old + new }
  end
end
于 2013-08-22T16:05:17.070 回答
0

这个也有效

arr.group_by{|t| t[:stemp]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :stemp ? v1 : v1+v2  }}}

更改为:id

arr.group_by{|t| t[:id]}.map {|key, value| value.inject({}) { |hash, values| values.merge(hash){ |key, v1, v2| key == :id ? v1 : v1+v2  }}}
于 2013-08-22T16:25:27.067 回答
0
[{"4"=>"20.0"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0", "5"=>"10.0"}, {"4"=>"10.0", "5"=>"0.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0", "5"=>"10.66"}, {"4"=>"20.0"}, {"4"=>"10.0"}, {"4"=>"10.0"}, {"4"=>"0.0"}].map{|m| m.map{|k,v| v.to_f}.sum()}.sum()
于 2016-12-22T09:29:47.203 回答