#29
May 09, 2007

group_by Month

Learn how to use the very useful group_by method to group an array by anything you want! In this episode I group an array of tasks by month then sort it properly.
Download (30.3 MB, 5:11)
alternative download for iPod & Apple TV (10.7 MB, 5:11)
# tasks_controller.rb
def index
  @tasks = Task.find(:all, :order => 'due_at, id', :limit => 50)
  @task_months = @tasks.group_by { |t| t.due_at.beginning_of_month }
end
<!-- tasks/index.rhtml -->
<% @task_months.sort.each do |month, tasks| %>
  <h2><%= month.strftime('%B') %></h2>
  <% for task in tasks %>
    <div class="task">
      <strong><%= task.name %></strong>
      due on <%= task.due_at.to_date.to_s(:long) %>
    </div>
  <% end %>
<% end %>

RSS Feed for Episode Comments 37 comments

1. chineseGuy May 09, 2007 at 01:05

haha,I learn some new tip,thanks!~
哈哈,我又学到新技巧啦,谢谢!~


2. eTrueke May 09, 2007 at 01:42

Really a good tip!

I love your blog. Congratulations.


3. Oskar May 09, 2007 at 02:59

Great episode as always


4. Jake May 09, 2007 at 06:40

Another good tip! Thanks!


5. Dave May 09, 2007 at 07:50

Great stuff. Gonna have to watch this one a few times :-)

As always, awesome job!


6. vigosan May 09, 2007 at 09:09

Wooow, I have no words !!!


7. Carl Porth May 09, 2007 at 10:40

I don't think you need that hack at the end, you should be able to do:

<% @task_months.sort { |a,b| a[0] <=> b[0] }.each do |month, tasks|

When you call sort on a hash it will just convert it to an array and do what you originally wanted.


8. Ryan Bates May 09, 2007 at 10:45

Hi Carl, I knew there must be another way to do that. Thanks for the tip!

I'm not sure if it's any cleaner than the other hack, but I can imagine extracting this into a custom Hash method to keep the view nice and clean.


9. Carl Porth May 09, 2007 at 10:47

I like that idea, perhaps sort_by_keys would be appropriate.


10. Ryan Bates May 09, 2007 at 10:50

Just found this which I think is the best solution:

@task_months.sort_by(&:first).each do |month, tasks|

Edit: Actually, it looks like "sort" by itself automatically sorts by key:

@task_months.sort.each do |month, tasks|


11. Carl Porth May 09, 2007 at 10:57

Ah, much better. We should pair program more often.


12. Steven Soroka May 09, 2007 at 13:18

Instead of grouping by date.beginning_of_month, you could just group by date.month :)


13. Ryan Bates May 09, 2007 at 13:53

@Steven, the reason I chose beginning_of_month is because it allows you to easily format the month however you want in the view. You can use the short month name, full month name, include the year, etc. The "month" method just returns the month as an integer so you lose the year information.


14. Clayton May 09, 2007 at 19:06

The sound seemed a little "fuzzy" on this one... other than that I've learned ANOTHER new something about rails. Thanks!


15. jjk2 May 09, 2007 at 20:46

ryan, how r u able to make ur screencasts so high quality, yet file size so small? can this be achieved with formats other than .mov ? what program do you use ?


16. Ryan Bates May 09, 2007 at 20:58

@Clayton, hmm, not sure why it's fuzzy. It sounds okay here and I didn't do anything different. Oh well, hope it's not a regular occurrence.

@jjk2, I'm using the Animation compression setting in QuickTime. The secret is to set the keyframe rate way up (about 1500) and it should make decent size files.


17. Carlos May 10, 2007 at 15:48

I really love your blog. Keep your good job job! Congratulations!


18. jeff May 11, 2007 at 10:40

Hi I watch this railscast religiously. It has taught me a lot. I really appreciate the excellent explanations for why you are doing something not only in rails but with ruby in general. I just spent about three hours figuring this little thing out so I figured i'd post it.

My application calls for a day of the week select box. there is no real helper for this. Here is the code I use..

<select>
<% dates = Date.const_get :DAYS%>
<%= options_from_collection_for_select(dates.sort{|x,y| x.last<=>y.last},'last','first',@preference.autosendday) %>
</select>

hope this helps someone save a minute of there programming life


19. jeff May 11, 2007 at 10:51

even better

<%= options_from_collection_for_select((Date.const_get :DAYS).sort_by(&:first),'last','first',@preference.autosendday) %>


20. Slaptijack May 11, 2007 at 11:14

As always, good work.


21. james May 12, 2007 at 14:57

lovely technique. can't believe how simple it is when you know the trick! replaced a horrible, horrible hack job i'd written in my ignorance.

so .. any hints on how you'd paginate that page you generate?

thanks for the wonderful videos, they're really great.


22. Ryan Bates May 12, 2007 at 22:45

@james, good question about pagination. It's not too tricky, you just have to make sure you order them properly so the grouped items are close together. For example, in this case I would need to group them by due_at so the month names stick together. Then you would treat it like normal. The only problem is a page my split up a group, but I can't think of an easy way to resolve this problem.

If you need to group items already, pagination may not be the best solution. Instead consider creating some sort of index of groups where clicking one will take you to the items in that group. In this case I would make a list of months, and clicking one would reveal the tasks for that month. This way the person doesn't have to deal with guessing which page has a certain month on it.


23. Marcel May 20, 2007 at 14:36

Awesome tip! I didn't know this already existed, so I implemented it all by myself ;)
Thanks for this video and your great blog.
Keep it up it's great stuff!


24. untaldouglas Jul 12, 2007 at 18:54

Hola !
Thanks for your screencasts, they rock !

Would you please consider to do an episode that include geting totals by groups, in a way that a parameter for filtering could be pass ...
Like for producing something like :

Month No of episodes Sum of Donations($)
January 23 3,500
Febreary 5 1,000
March 0 500
.......
Total 999 9,999.99

Gracias !


25. Johan Sep 13, 2007 at 03:11

Great podcasts!

How would you solve the problem if we wanted to group by year also. Like:

2006
Jan
Dec
2010
Jan
Feb

Where should i put the group_by year


26. Ryan Bates Sep 13, 2007 at 09:48

@Johan, try something like this:

http://pastie.caboo.se/96864

That is really ugly in the view, so what I'd probably do is move this into a helper + partial.


27. Peter Roome Dec 05, 2007 at 02:28

This is a very handy little tip, thanks. Keep up the good work with the casts too.

Just one question: How would you go about producing a list of the months so that every month of the year was listed whether it has anything to group under it or not? (Does that make sense?!?!)

Cheers


28. William Monk Dec 06, 2007 at 10:49

how could i go about reversing the whole thing, so if i had posts from Descember and November, it would show:
December
...
..
November
...
..
October
etc?


29. cover Dec 25, 2007 at 01:46

@William Monk

You just need a reverse :)

<% @task_months.keys.sort.reverse.each do |month| %>


30. iratik Jul 10, 2008 at 04:26

I don't see where anyone else has mentioned this but
@task_months.keys.sort would just sort the months in alphabetical order. Maybe you need something that looks at the arrays pointed to by the month names and looks at the first element of each array (a time object) to see if its newer or older than the next. Then loop through the resulting array of ordered value sets and lookup the correct index for the section title.

@task_months.values.sort{|a,b| a[0]<=>b[0]}.each{|tval|
 @task_months.index(tval) ...


31. victornegri Nov 07, 2008 at 07:43

Ryan, great episode. I'm new to Rails and your Railscasts have helped me a lot!

iratick, can you expand on your comment (or can someone expand on his comment)? I think that's what I want to do with my code but don't know how to do it.

I want to group_by a certain value in an object but I don't want to sort by it. I'd like to sort by another object value.


32. nandan pramanik Dec 16, 2008 at 21:29

i get events, and group_by start_date it shows the the order of day like saturday, friday, trusday ..., How i get the data as reverse order?


33. Michael Oct 10, 2009 at 14:01

Ryan. Thank you.
You saved me hours with this link:
http://pastie.caboo.se/96864


34. snapneck Mar 22, 2010 at 05:39

This is gold. Thank you.


35. Gang Mar 26, 2010 at 23:35

That good idea.
Thank you.


36. Jim Knight Apr 21, 2010 at 18:54

I have an issue where my group_by list has numbers and alpha characters so it's sorting like this:
10
2
bakery
produce

but I want it to sort
2
10
bakery
produce

<% @items_aisles.keys.sort.each do |aisle| %>

so i found a place where someone said you could do :order => '0 + name ASC' etc but that's not relevant in this context. So I'm perplexed. Any help? Thanks.

Ryan, love the screencasts, learned a lot!


37. Jim Knight Apr 24, 2010 at 05:49

ok found this natural sort plugin and it worked great as a replacement for the keys.sort.
http://github.com/logandk/natural_sorting


38. Ramon Jun 13, 2010 at 23:31

The link above with grouping by year then month isn't formatted properly. Maybe pastie has changed their parsing since it was created.

Go to http://pastie.org/1003477 instead.


39. ed hardy 1swimwear Jul 20, 2010 at 00:17

454123


40. tiffany notes Jul 30, 2010 at 02:27

Great site. This could probably have the refactoring tag added t it.


41. Jordan Air Retro Aug 01, 2010 at 23:25

I want to group_by a certain value in an object but I don't want to sort by it. I'd like to sort by another object value.


42. timberlandbootsuk Aug 02, 2010 at 02:06

we provide our buyers with an efficient and manageable procurement process covering every phase of the international supply chain and

streamlining trade channels. Also welcome wholesaling, feedback now!


44. makeup brushes Aug 08, 2010 at 22:19

Very interesting and useful information! It's pity that such occasions are still exist in the world, but we should act all together for to prevent them. Thanks.


45. tow-rope Aug 08, 2010 at 22:24

I definitely love your own posting style, very interesting. don’t give up and also keep posting as it just simply that is worth to read it,excited to looked over far more of your articles, have a good one.


46. p90 workout Aug 12, 2010 at 09:18

I found your blog on Yahoo and I just wanted to say that I think your writing is simply stunning! Thanks again for providing this content for free.


47. nike dunks low Aug 14, 2010 at 01:27

Don’t give up and also keep posting as it just simply that is worth to read it,excited to looked over far more of your articles, have a good one.I want to group_by a certain value in an object but I don't want to sort by it. I'd like to sort by another object value.


48. supra tk society Aug 18, 2010 at 18:51

good job,good article


49. louis vuitton shoes Aug 26, 2010 at 21:02

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


50. snow boots Aug 31, 2010 at 01:52

When you call sort on a hash it will just convert it to an array and do what you originally wanted.


51. Cheap Supra Shoes Sep 01, 2010 at 00:49

Benefit a lot from this website.


52. GHD Hair Straighteners Sep 01, 2010 at 00:49

I would to know more from here.


53. GHD Australia Sep 01, 2010 at 00:51

Hope to share more content with us


54. Vertu Ascent Ti Sep 01, 2010 at 02:03

Make persistent efforts. Emperor it pays off.


55. Nike Air Max 90 Sep 01, 2010 at 02:13

God rewards the diligent.


56. louis vuitton sunglasses Sep 01, 2010 at 21:42

Good post, I can’t say that I agree with everything that was said, but very good information overall:)


57. Dolce & Gabbana Belts Sep 01, 2010 at 22:58

Thanx for the effort, keep up the good work Great work, I am going to start a small Blog Engine course work using your site I hope you enjoy blogging with the popular BlogEngine.net.Thethoughts you express are really awesome. Hope you will right some more posts.

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source