#28 in_groups_of
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 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 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.
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 nil
s. 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.