2013-08-22 4 views
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) в качестве поля идентификатора; если есть еще один хэш с этим идентификатором, как и в моем примере выше, два хэша должны быть добавлены вместе.

ответ

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 

Просто скопируйте вставить его в IRB консоли с рубином 1.9.3, выводит это:

[ 
    {: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 }}} 

изменение: ид

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() 
Смежные вопросы