How to group Rails ActiveRecord results by a column?

ruby, ruby-on-rails

Solution

You can have the database do this for you like so:

@yesterdays_clients = Client.includes(:tasks).merge(Task.yesterday).order(:name)

Besides being cleaner, it's more efficient since it gets all your clients and tasks in one pass. The original code was subject to N+1 queries because there was no eager loading.

BTW, you can make your scope simpler as well:

scope :yesterday, -> { where(:start => (Date.yesterday.to_time...Date.today.to_time)) }

Problem

I'm working on a little time tracking application and ran into a problem I don't know how to solve. I have a `Task` model and a `Client` model. Each task belongs to a client. ``` class Task < ActiveRecord::Base belongs_to :client attr_accessible :client_id, :description, :start, :end scope :yesterday, -> { where('start > ?', Date.yesterday.to_time).where('start < ?', Date.today.to_time) } end class Client < ActiveRecord::Base attr_accessible :name has_many :tasks end ``` Right now I'm displaying a list of tasks scoped by the day the tasks were completed, and ordered by the time they were completed. I would like to display that same list, but grouped by the client, and sorted by the client name. Here's what I would like to do: ``` <div id="yesterday_summary"> <% @yesterday_clients.each do |client| %> <h2><%= client.name %></h2> <ul> <% client.tasks.each do |task| %> <li><%= task.description %></li> <% end %> </ul> <% end %> </div> ``` In my controller I currently have: ``` @tasks_yesterday = Task.yesterday @yesterday_clients = group_tasks_by_client @tasks_yesterday ``` And in the `group_tasks_by_client` method, I have some pretty ugly code that isn't even working at the moment: ``` def group_tasks_by_client(tasks) clients = [] tasks.collect(&:client).each do |client| clients << {client.id => client} unless clients.has_key? client.id end clients_with_tasks = [] clients.each do |client| c = Struct.new(:name, :tasks) cl = c.new(client.name, []) tasks.each do |task| cl.tasks << task if task.client_id = client.id end clients_with_tasks << cl end clients_with_tasks end ``` I'm sure there is a clean, simple, rails-way to do this but I'm not sure how. How can this be done?

Original source