2013-08-22 52 views
1

基本上我有散列的像这样的数组:Rails的总和的阵列内散列

[ 
    { :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的散列,就像我上面的例子,这两个散列应该加在一起。

回答

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} 
] 
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 } 
] 
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 
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 }}} 
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()