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.
@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.
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 ?
@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.
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..
@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.
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!
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
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?!?!)
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?
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.
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.
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.
Below is the code updated for Rails 4.2. Note: the Rails api says that the group_by method is deprecated in Rails, but don't be confused by that, they just mean that you use the Ruby group_by method instead of the Rails' modified version (which no longer exists).
<%@task_months.each do |month, tasks| %><h2><%= month.strftime('%B') %></h2><% tasks.each do |task| %><divclass="task"><strong><%= task.name %></strong>
due on <%= task.due_at.to_date.to_s(:long) %></div><%end%><%end%>
Really a good tip!
I love your blog. Congratulations.
Great episode as always
Great stuff. Gonna have to watch this one a few times :-)
As always, awesome job!
Wooow, I have no words !!!
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.
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.
I like that idea, perhaps sort_by_keys would be appropriate.
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|
Ah, much better. We should pair program more often.
Instead of grouping by date.beginning_of_month, you could just group by date.month :)
@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.
The sound seemed a little "fuzzy" on this one... other than that I've learned ANOTHER new something about rails. Thanks!
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 ?
@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.
I really love your blog. Keep your good job job! Congratulations!
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
even better
<%= options_from_collection_for_select((Date.const_get :DAYS).sort_by(&:first),'last','first',@preference.autosendday) %>
As always, good work.
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.
@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.
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!
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 !
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
@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.
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
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?
@William Monk
You just need a reverse :)
<% @task_months.keys.sort.reverse.each do |month| %>
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) ...
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.
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?
Ryan. Thank you.
You saved me hours with this link:
http://pastie.caboo.se/96864
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!
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.
Thank you Ramon updating pastie link
(Go to http://pastie.org/1003477 instead.)
THIS WAS A HUGE TIME SAVER! And thank you so much R.B.! Your work has helped me a lot!
Thanks a lot! I know, I'm a digger, but it's still working! Saved my life. :)
Below is the code updated for Rails 4.2. Note: the Rails api says that the group_by method is deprecated in Rails, but don't be confused by that, they just mean that you use the Ruby group_by method instead of the Rails' modified version (which no longer exists).
This episode has been updated for Rails 5 as a blog post. Group By Month in Rails 5