homeASCIIcasts

28: In Groups Of 

(view original Railscast)

Other translations: It

Below is an application showing a list of tasks. The tasks are rendered in a table in one column. We could make better use of the space in the page by rendering them in multiple columns, but what’s the best way to do that? Rails provides a method called in_groups_of as an extension to the Array object that helps us to do this.

Our list of tasks in a single column.

Our task list in a single column.

To demonstrate how in_groups_of works we’ll show it in action in the console.

>> a = (1..12).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
>> a.in_groups_of(4)
=> [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
>> a.in_groups_of(3)
=> [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]

In the first row above we create an array with twelve elements from a range. The in_groups_of method can be used with a single argument to return an array of arrays, each containing n elements, where n is the value of the argument passed. In the code above we split the array first into three groups of four and then into four groups of three. (Note that in_groups_of returns a copy of the array and doesn’t alter the original.)

Displaying Our Tasks In Columns

Now that we know how to use in_groups_of we can use it in our view code to display our task list in columns. The relevant part of our index view currently looks like this.

<table>
    <% @tasks.each do |task| %>
    <tr>
      <td><%= task.name %></td>
    </tr>
    <% end %>
</table>We’ll change it to look like this.
<table>
    <% @tasks.in_groups_of(4) do |tasks| %>
    <tr>
      <% tasks.each do |task| %>
      <td><%= task.name %></td>
      <% end %>
    </tr>
    <% end %>
</table>

We can use a block with in_groups_of, so we’ll split our tasks into groups of four, output an opening tr tag, then loop through each task in that group and output it between td tags. The result is shown below.

Our tasks are now rendered in four columns.

Our tasks are now rendered in four columns as we wanted, but we’re not quite there yet. As we’re rendering twelve tasks in groups of four, we have three full rows of tasks. If we add a couple more and refresh the page the following happens.

The application throws an error if the number of tasks doesn’t divide exactly into the groups.

We can see the reason for this if we go back to the console and try to split our array of twelve numbers into groups of five.

>> a = (1..12).to_a
>> a.in_groups_of(5)
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, nil, nil, nil]]

If there aren’t enough elements to fill the last array, in_groups_of will pad it with nils. To stop this, we can pass a second argument. If the second argument is false then the last array will be shorter than the others; if we pass any other value then the last array is padded out with that value.

>> a.in_groups_of(5, false)
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12]]
>> a.in_groups_of(5, 0)
=> [[1, 2, 3, 4, 5], [6, 7, 8, 9, 10], [11, 12, 0, 0, 0]]
>> 

If we use false as the second argument to our in_groups_of in the view code then the page will render no matter what the number of tasks we have.

Now our application can handle any number of tasks.