Flashback time. We are in 1999 and you are coding in ASP thinking that this language is the future. You use ADODB recordsets to iterate over collections. You have a recordset containing some records from a “quotes” table. The “quotes” table contains a author column (varchar) and a body column (varchar). Now you want to display the results grouped by author name so it looks like this :
Georges Brassens
– Les filles quand ça dit “je t’aime”, c’est comme un second baptême
– Aucune idée sur terre n’est digne d’un trépas
Billie Holiday
– Don’t threaten me with love, baby. Let’s just go walking in the rain.
– I never hurt nobody but myself and that’s nobody’s business but my own.
Assuming your recordset is ordered by author, you do something like this (remember, we’re in 1999) :
[vb]<TABLE style="color:fushia;font-style:MSONormal generated=frontpage">
<%while not objRS.eof %>
<%if(current_author <> objRS("author"))%>
<h1><%=objRS("author")%></h1>
<%end if%>
<Tr><td><%=objRS("body")</td></TR>
<%current_author = objRS("author")%>
<%objRS.MoveNext%>
<%loop%>
<%objRS.close%>
</TABLE>[/vb]
Ok welcome back in 2008. ASP is dead. You are coding in rails and you want to do the same thing. How will you do it? Storing the author name in a buffer variable like in 1999? Not too sure about it.
This is a job for Enumerable#group_by (Enumerable is a module that is mixed in the Array class)
all_quotes = Quote.find(:all)
@authors = all_quotes.group_by(&:author)
Enumerable#group_by will create different sets of quotes based on the “author” values. Why the “&” sign? It’s because group_by expects a block. I could have done it this way : @authors = all_quotes.group_by{|quote| quote.author}
So @authors is now a hash that will look like this:
{“George Brassens” => [#<Quote id:131 …>, #<Quote id:331 …>], “Billie Holiday” => [#<Quote id:111 …>, #<Quote id:911 …>] }
Now you can iterate over it like this :
@authors.each_pair do |author_name, quotes|
<h1><%=author_name%></h1>
<%quotes.each do |quote| %>
- <%=quote.body%><br />
<%end%>
<%end%>
One more thing to note : @authors is a hash, and hashes cannot be ordered. If you want to display quotes by sorted author name, you could do this :
@authors.keys.sort.each do |author_name|
<h1><%=author_name%></h1>
<%@authors[author_name].each do |quote|%>
- <%=quote.body%><br />
<%end%>
<%end%>
Note : Enumerable#group_by does not exist in ruby 1.8, it only exists in Rails. The method will be in ruby 1.9 however.
You can also do:
@authors.sort.each do |author_name, quote|
because Hash#sort returns an array of 2-value arrays of the form [key, value]
Grant, this is even better than @authors.keys.sort.each, thanks for sharing.
Thanks for this, very helpful for us who don’t yet know all the nooks and crannys in rails and ruby!
It is always interesting to learn new tricks but in this case I would consider more efficient to get the data ready to use straight from the DB rather than getting the same data and reorganize in memory. I would do something like this:
@authors = Author.find :all, :include => :quotes, :order => :name
this returns an array of authors and pre-load their quotes using two sql instances such as:
SELECT * FROM `authors` ORDER BY name;
SELECT `quotes`.* FROM `quotes` WHERE (`quotes`.author_id IN (1,2,3,…));
in the view you just loop all authors and their quotes:
The form eaten my html example 🙁
I hope you get the idea
This article will help the internet viewers for creating new weblog or even a weblog from start to end.