Headers per date in Haml: How to create a Haml view that summarizes items by date

posted 2012-Feb-14

You have a Ruby web application that uses Haml—be it Rails, Sinatra, or some other framework. You have a list of timestamped items that you want to output with a separate header for each day. You want output like this:

<h2>2012-Feb-14</h2>
<ul>
  <li><a href="/item/14">Good Stuff</a></li>
  <li><a href="/item/7">More Good Stuff</a></li>
</ul>
<h2>2012-Feb-13</h2>
<ul>
  <li><a href="/item/2">Older Thingy</a></li>
</ul>

In ERB you might do something horrible like so:

<%
unless @items.empty?
  last_date = nil
  @items.sort_by(&:time).each do |item|
    date = item.time.strftime('%Y-%b-%d')
    if date != last_date
      if last_date then
        %></ul><%
      end
      %><h2><%=date%></h2><ul><%
      last_date = date
    end
    %><li><a href="/item/<%=item.id%>"><%=item.name%></a></li><%
  end
  %></ul><%
end
%>

Using Enumerable#group_by and Haml, however, you can make something far more elegant:

- @items.sort_by(&:time).group_by{ |item| item.time.strftime('%Y-%b-%d') }.each do |day,day_items|
  %h2= day
  %ul
    - day_items.each do |item|
      %li
        %a{href:"/item/#{item.id}"}= item.name

Let Ruby create the sub-groups for you! You don’t even need the test for empty? that you need in ERB, since an empty array will create an empty Hash, and it will never be iterated.

net.mind details other résumé contact
Phrogz.net