Cameron Cameron - 1 month ago 14
Ruby Question

Return how much data grouped by in Rails

I have the following Ruby code which groups JSON data into months:

data = data.group_by { |d| d['Date'].to_date.strftime('%Y-%m') }
.collect { |k, v| {Date: k, Total: v.sum { |d| d['Total'].to_i }} }


So for example it will change:

{"Date":"2012-04-01 12:00:00","Total":"50"},
{"Date":"2012-04-01 13:00:00","Total":"50"},
{"Date":"2012-04-02 05:00:00","Total":"100"},
{"Date":"2012-04-02 06:00:00","Total":"100"}


Into:

{"Date":"2012-04","Total":"300"}


But I'd like to know how many days were grouped into the new JSON.

So again in my above example, I'd want to return:

{"Date":"2012-04","Total":"300","Days":"2"}


Edit: I accidentally thought I wanted the total items grouped originally, but I've edited this question, as what I really want to know, is the total days grouped into the month (as some months might not have data for all the days so I want to know how complete the month is).

The data always comes as hourly! So don't need to worry about minutes or seconds.

Answer

Knowing that group_by returns a hash where the keys are evaluated result from the block, and values are arrays of elements in enum corresponding to the key, then your collect received a |k,v| where the k is the month and v is an array of items in that month.

You can use that to change you code like this:

data = data.group_by { |d| d['Date'].to_date.strftime('%Y-%m') }
           .collect { |k, v| {Date: k, Total: v.sum { |d| d['Total'].to_i }, Grouped: v.count} }

Updated answer

Ok, based on your comments, here's my updated answer. This code works but it is not the most elegant. There might be a better way to achieve this. But I hope this can help you anyway.

totals = {}
data2 = data.group_by { |d|
  day = d['Date'].to_date.strftime('%Y-%m-%d')
  totals[day] ||= 0
  totals[day] += 1
  d['Date'].to_date.strftime('%Y-%m')
}.collect { |month, values|
  {
    Date: month,
    Total: values.sum { |d| d['Total'].to_i},
    Grouped: totals.group_by{|day, v| day.to_date.strftime('%Y-%m')}[month].count
   }
}

This keeps track of each day in a separate hash totals and then count the days in a specific month.


Slightly more elegant

days = []
data2 = data.group_by { |d|
  day = d['Date'].to_date.strftime('%Y-%m-%d')
  days << day
  d['Date'].to_date.strftime('%Y-%m')
}.collect { |month, values|
  {
    Date: month,
    Total: values.sum { |d| d['Total'].to_i},
    Days: days.uniq.select{|day| day.to_date.strftime('%Y-%m') == month}.count
   }
}
Comments