Skip to main content

Group records by month to display in a chart

General • Asked by Francisco Quinones

Hi Chris Im using HighChart and I have some data that want to display in group by month.


You may have to adjust the hashes for HighCharts, but the groupdate gem is pretty good for this. It was designed for the Chartkick gem. https://github.com/ankane/groupdate


more details of my problem. Im looking for a way to make the code more dry, has I need to display all the Task, and Task by status. So your getting a bar chart with Task complete, Task Pending and Task cancel. With this code I can display alll task. so now how can I use this code but to display each status on the chart. without duplicating the code.

Controller

@months = Array.new
@total_tasks = Array.new
@tasks = Task.group_by { |t| t.start_date.strftime("%B/%Y")} 
@tasks.reverse_each do |key, value|
    @months << key
    @total_tasks << value.size
end

With this line I get the same output

   Task.group_by_month(:date, format: "%b %Y").count

VIEW

<div id="chart_tasks" style="min-width: 210px; max-width: auto; height: 400px; margin: 0 auto"></div>
<%= javascript_tag do %>
   window.months = JSON.parse('<%= raw @months%>')
   window.total_tasks = JSON.parse('<%= raw @total_tasks%>')
<% end %>

$(function () {
    $('#chart_tasks').highcharts({

        xAxis: {
            categories: months
        }, 

        series: [{
                name: ' Task Total',
                type: 'column', 
                data: total_tasks,
                dataLabels: {
                        enabled: true,
                        color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'black'
                    }
            }
    });
});

I think you'll need to filter the array of tasks for each status, create variables for each of those, and then pass those in as different series to your chart.


    @finish , @unfinish = [], []
@tasks.each do |t|
    @finish << t if t.status == 'Complete'
    @unfinish << t if t.status == 'Cancel'
end

You can make that a bit easier if you do select too:

@tasks      = Task.group_by { |t| t.start_date.strftime("%B/%Y")} 
@finished   = @task.select{ |task| task.status == 'Complete' }
@unfinished = @task.select{ |task| task.status == 'Cancel' }

Nice more clean love it what I was looking. Chris Thank you for all the help for the pass few months. my app looks great with all the help.


You're welcome man! :)


Chris can I use any of this scope for that??

scope :cancel, lambda {where(:status => "Cancelada")}
scope :unfinish, lambda {where(:status => "Expirada")}
scope :pending, lambda {where(:status => "Pendiente")}
scope :finish, lambda {where(:status => "Completada")}
scope :transfer, lambda {where(:move_task => true)}

Yup! I would recommend doing scopes for this for sure.


Chris if I do.
@tasks = Task.group_by { |t| t.start_date.strftime("%B/%Y")}
@finished = @tasks.select{ |task| task.status == 'Complete' }

I get a error

   NoMethodError: undefined method `status' for "November/2015":String
from (irb):136:in `block in irb_binding'
from (irb):136:in `select'

I think that @tasks its using a K,V and when im reading the .select{ |task| task.status} its doing this

 k = "November/2015"
 k.status

Ah yeah, your group_by is going to give you a hash. You probably want to do group_bys on those after filtering the queries to make it cleaner.


Chris How can I filter the queries with out duplicating the code so much as Im breaking my head. I like to keep it all dry up and few querys
can you giving me a example of your approach. thankyou.


I would do something like this (using the group_by_month method from groupdate so it's a bit cleaner).

@tasks    = Task.all.group_by_month(:date, format: "%b %Y").count
@finished = Task.finish.group_by_month(:date, format: "%b %Y").count
@unfinish = Task.unfinish.group_by_month(:date, format: "%b %Y").count

thankyou thats the best solution.


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.