I have an array of hashes with the keys being countries and the values being number of days.
I would like to aggregate over the hashes and sum the values for the countries that are the same.
the array could look like this countries = [{"Country"=>"Brazil", "Duration"=>731/1 days}, {"Country"=>"Brazil", "Duration"=>365/1 days}]
I would like this to return something on the lines of: [{"Country" => "Brazil", "Duration"=>1096/1 days}]
I tried the other questions on SO like this one
countries.inject{|new_h, old_h| new_h.merge(old_h) {|_, old_v, new_v| old_v + new_v}}
Produces {"Country"=>"BrazilBrazil", "Duration"=>1096/1 days}
Is there a way to selectively only merge specific values?
This uses the form of Hash::new that creates a creates an empty hash with a default value (here 0
). For a hash h
created that way, h[k]
returns the default value if the hash does not have a key k
. The hash is not modified.
countries = [{"Country"=>"Brazil", "Duration"=>"731/1 days"},
{"Country"=>"Argentina", "Duration"=>"123/1 days"},
{"Country"=>"Brazil", "Duration"=>"240/1 days"},
{"Country"=>"Argentina", "Duration"=>"260/1 days"}]
countries.each_with_object(Hash.new(0)) {|g,h| h[g["Country"]] += g["Duration"].to_i }.
map { |k,v| { "Country"=>k, "Duration"=>"#{v}/1 days" } }
#=> [{"Country"=>"Brazil", "Duration"=>"971/1 days"},
# {"Country"=>"Argentina", "Duration"=>"383/1 days"}]
The first hash passed to the block and assigned to the block variable g
.
g = {"Country"=>"Brazil", "Duration"=>"731/1 days"}
At this time h #=> {}
. We then compute
h[g["Country"]] += g["Duration"].to_i
#=> h["Brazil"] += "971/1 days".to_i
#=> h["Brazil"] = h["Brazil"] + 971
#=> h["Brazil"] = 0 + 971 # h["Brazil"]
See String#to_i for an explanation of why "971/1 days".to_i
returns 971
.
h["Brazil"]
on the right of the equality returns the default value of 0
because h
does not (yet) have a key "Brazil"
. Note that h["Brazil"]
on the right is syntactic sugar for h.[]("Brazil")
, whereas on the left it is syntactic sugar for h.[]=(h["Brazil"] + 97)
. It is Hash#[] that returns the default value when the hash does not have the given key. The remaining steps are similar.
You may update your code as follows:
countries.inject do |new_h, old_h|
new_h.merge(old_h) do |k, old_v, new_v|
if k=="Country" then old_v else old_v + new_v end
end
end
# => {"Country"=>"Brazil", "Duration"=>1096}
where you basically use the k
(for key ) argument to switch among different merging policies.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.