When the values of a single key in a collection of hashes are to be totaled I usually begin by constructing a counting hash:
h = (a+b+c).each_with_object({}) do |g,h| h[g[:name]] = (h[g[:name]] || 0) + g[:value] end #=> {"Identifier"=>1500, "Identifier2"=>150}
Note that if h does not have a key g[:name], h[g[:name]] #=> nil, so:
h[g[:name]] = (h[g[:name]] || 0) + g[:value] = (nil || 0) + g[:value] = 0 + g[:value] = g[:value]
We may now easily obtain the desired result:
h.map { |(name,value)| { name: name, value: value } } #=> [{:name=>"Identifier", :value=>1500}, # {:name=>"Identifier2", :value=>150}]
If desired these two expressions can be chained:
(a+b+c).each_with_object({}) do |g,h| h[g[:name]] = (h[g[:name]] || 0) + g[:value] end.map { |(name,value)| { name: name, value: value } } #=> [{:name=>"Identifier", :value=>1500}, # {:name=>"Identifier2", :value=>150}]
Sometimes you might see:
h[k1] = (h[k1] || 0) + g[k2]
written:
(h[k1] ||= 0) + g[k2]
which expands to the same thing.
Another way to calculate h, which I would say is more "Ruby-like", is the following.
h = (a+b+c).each_with_object(Hash.new(0)) do |g,h| h[g[:name]] += g[:value] end
This creates the hash represented by the block variable h using the form of Hash::new that takes an argument called the default value:
h = Hash.new(0)
All this means is that if h does not have a key k, h[k] returns the default value, here 0. Note that
h[g[:name]] += g[:value]
expands to:
h[g[:name]] = h[g[:name]] + g[:value]
so if h does not have a key g[:name] this reduces to:
h[g[:name]] = 0 + g[:value]
If you were wondering why h[g[:name]] on the left of the equality was not replaced by 0, it is because that part of the expression employs the method Hash#[]=, whereas the method Hash#[] is used on he right. Hash::new with a default value only concerns Hash#[].
{name: 'Identifier', value: ...}always the first element in all 3 arrays,{name: 'Identifier2', value: ... }always the second?