Shpigford
Shpigford

Reputation: 25348

Group & sum hashes by month, including when no data

I have a date range that I'm using to query a database with. The returned result is an array of hashes like so:

[
  {:created=>"2013-12-10", :amount=>1}, 
  {:created=>"2014-02-20", :amount=>1}, 
  {:created=>"2014-02-23", :amount=>4}, 
  {:created=>"2014-02-24", :amount=>1}, 
  {:created=>"2014-03-06", :amount=>1}, 
  {:created=>"2014-03-14", :amount=>3}, 
  {:created=>"2014-03-15", :amount=>1}, 
  {:created=>"2014-03-17", :amount=>1}, 
  {:created=>"2014-03-20", :amount=>1}, 
  {:created=>"2014-03-21", :amount=>1}, 
  {:created=>"2014-03-24", :amount=>1}, 
  {:created=>"2014-03-25", :amount=>1}, 
  {:created=>"2014-03-28", :amount=>1}, 
  {:created=>"2014-04-05", :amount=>1}, 
  {:created=>"2014-04-07", :amount=>1}
] 

What I need to do is group and sum the amount by month, and in the case where a month has no data but is part of the initial range, show 0.

So in my example above, the date range I queried the database with was 2013-11-13 to 2014-04-18.

Output is ultimately just a basic array of the data grouped month and summed. So the example above should produce:

[0, 1, 0, 6, 11, 2]

Those array items coordinate to: November, December, January, February, March, April.

FWIW, I'm running Ruby 2.0.0 and this is part of a Rails 4 app, if there happen to be some special Rails helper methods that would be useful here.

Upvotes: 0

Views: 1061

Answers (1)

Малъ Скрылевъ
Малъ Скрылевъ

Reputation: 16507

You can use #group_by methods to group by year, and month, and then combination of #map, #uniq, and #reduce to get proper Array:

# grouping record by year and month, and then sorting them
g = a.group_by {|v| Date.parse(v[:created][0,7] + '-01') }.sort

# generating a result hash
h = Hash[g]

# generating range
range = Date.new(2013,11)..Date.new(2014,04)

# here we firstly remap to get *year-month* list by dates, which will contain only
# one date per month, then we calculate sum for each month value.
range.to_a.map {|d| Date.new(d.year,d.month,1)}.uniq.map {|d| h[d] && h[d].reduce(0) {|sum,h| sum + h[:amount]} || 0 }

# => [0, 1, 0, 6, 11, 2] 

for you can use #try method:

range.to_a.map {|d| Date.new(d.year,d.month,1)}.uniq.map {|d| h[d].try(:reduce, 0) {|sum,h| sum + h[:amount]} || 0 }

Upvotes: 1

Related Questions