devroom.io/content/posts/2007-01-12-rails-group-results-by-week-using-group_by.md

71 lines
2.5 KiB
Markdown
Raw Normal View History

2015-03-26 11:28:08 +00:00
+++
date = "2007-01-12"
title = "Rails: Group results by week (using group_by)"
tags = ["General", "Everything", "RubyOnRails", "Features"]
slug = "rails-group-results-by-week-using-group_by"
2017-09-11 12:20:15 +00:00
description = "How to group ActiveRecod results, for instance by week"
2015-03-26 11:28:08 +00:00
+++
The Enumerable class in Rails contains a method named 'group_by'. This method is pure magic for a developer's point of view. I'll give you a simple example that shows the power of group_by.
Let's say you have a table 'posts' containing blog posts. Now, you normally show these chronologically a few at a time. Nothing special there. For some special overview page, you want to group your posts by week.
With normal ActiveRecord operations this would be quite an elaborate task. But with group_by from Enumerable, it becomes child's play.
First of all, in the controller, just get all the posts you need. In this case, all of them:
Controller:
2017-03-20 15:35:19 +00:00
``` ruby
def list
@posts = Post.find :all
end
```
2015-03-26 11:28:08 +00:00
As you can see, I perform no ordering or whatsoever here.
Now, in your view you normally would iterate over all posts like this:
2017-03-20 15:35:19 +00:00
``` erb
<%= render :partial => 'post', :collection => @posts %>
```
2015-03-26 11:28:08 +00:00
But, as I said, we want to group the posts by week. To make life easy, I add a method to the Post class that returns the week number in which a post was written:
Model Post:
2017-03-20 15:35:19 +00:00
``` ruby
def week
self.created_at.strftime('%W')
end
```
2015-03-26 11:28:08 +00:00
Now, the magic will happen in our view:
2017-03-20 15:35:19 +00:00
``` erb
<% @posts.group_by(&:week).each do |week, posts| %>
<div id="week">
<h2>Week < %= week %></h2>
< %= render :partial => 'post', :collection => @posts %>
</div>
<% end %>
```
2015-03-26 11:28:08 +00:00
Let me explain the above. We specify that we want to call group_by for @posts. But we need to say how we want to group these posts. By specifying &:week we tell group_by that we want to group by the result of the week attribute of every post. This is the attribute we specified earlier in the model.
Well, when the grouping is done we create a block that will handle every group of items. We extract 'week' and 'posts' here. 'week' contains the week number and 'posts' all the posts for that week.
As normal, we can now show the week number and iterate over the posts.
## Sorting groups
The result of group_by is not guaranteed to be ordered in any way. Simply call 'sort' before each and you're set:
2017-03-20 15:35:19 +00:00
``` ruby
@posts.group_by(&:week).sort.each do |week, posts|
```
2015-03-26 11:28:08 +00:00
Mostly, you'll find that the posts for every group are not sorted either. With the example above I think it's easy to figure out how to do that now. (hint: .sort)