Splitting Hairs and Arrays

June 19 2008

Am I just dumb, or is it really a lot harder than it should be to break an array up into a set number of chunks?

For example, I have a list of 8 items that I want to break into 3 arrays, each displayed in their own unordered list, like this:

  • Item 1
  • Item 2
  • Item 3
  • Item 4
  • Item 5
  • Item 6
  • Item 7
  • Item 8

Brian and I spent a ridiculous amount of time (20 minutes, at least) trying to come up with a clean solution to this seemingly simple problem. The closest thing there is to a solution is Enumerable#each_slice in Ruby core or Array#in_groups_of in Active Support.

<% my_array.each_slice((my_array.size.to_f / 3).ceil) do |list| %>
  <ul>
    <% list.each do |item| %>
      <li><%= item %></li>
    <% end %>
  </ul>
<% end %>

or

<% my_array.in_groups_of((my_array.size.to_f / 3).ceil, false) do |list| %>
  <ul>
    <% list.each do |item| %>
      <li><%= item %></li>
    <% end %>
  </ul>
<% end %>

There’s not really a difference between either solution. Both requires that we calculate how many items we want in each list. (We convert the size to a float, divide by the number of columns, then round up. This gives us the same number of items in each column, with the last column having fewer.)

Our solution

We didn’t like having that much logic in the view, so we added a method to enumerable; we thought the division (/) method seemed appropriate since we’re dividing the array into equal parts.

module Enumerable
  # Divide into groups
  def /(num)
    returning [] do |result|
      each_slice((size.to_f / num).ceil) {|a| result << a }
    end
  end
end

Note: this method is now in our awesomeness plugin.

So now we can just divide our array into chunks in the view.

<% (my_array / 3).each do |list| %>
  <ul>
    <% list.each do |item| %>
      <li><%= item %></li>
    <% end %>
  </ul>
<% end %>

Are we dumb? Is there already a way to do this that wasn’t obvious to us and we just wasted our time (and I wasted even more time blogging about it)?

Update: Thanks to Aaron Pfeifer for pointing out the discussion on Jay Field’s blog about something similar. I’ve refactored this code in awesomeness to be more “robust’ (read: convoluted).
posted by brandon | updated June 19th 11:11 PM
comments feed

7 comments

  1. Something similar was actually talked about here: http://blog.jayfields.com/2007/09/ruby-arraychunk.html

    Aaron Pfeifer Aaron Pfeifer
    June 19, 2008 at 08:53 PM
  2. Facets has something like this. Uncovered this with Eli back when we reworking some Rake tasks: http://facets.rubyforge.org/doc/api/core/index.html

    That was way after I created a Multiterator to do the same thing. IT went something like: Multiterator(array, 3).each { |a, b, c| puts a + b + c }

    +1 for Ruby 1.9 having something that does this.

    Nolan Eakins Nolan Eakins
    June 19, 2008 at 11:19 PM
  3. Very well done code.. Thanks

    Sam Flowers, jr Sam Flowers, jr
    July 03, 2008 at 12:44 PM
  4. (“A”..”Z”).to_a.in_groups_of(3).transpose

    Andrew Vit Andrew Vit
    July 10, 2008 at 07:57 PM
  5. Andrew,

    Except that I want them group as A-I, J-R, and S-Z, not every 3rd letter.

    Brandon Brandon
    July 11, 2008 at 08:09 AM
  6. Brandon: Andrew’s is almost right, isn’t he? Just skip the transpose (which I didn’t know about incidentally) and it works…

    (“A”..”Z”).to_a.in_groups_of(3) [“D”, “E”, “F”], [“G”, “H”, “I”], [“J”, “K”, “L”], [“M”, “N”, “O”], [“P”, “Q”, “R”], [“S”, “T”, “U”], [“V”, “W”, “X”], [“Y”, “Z”, nil]]

    Bill Horsman Bill Horsman
    July 13, 2008 at 03:31 AM
  7. Bill,

    Not quite. I want 3 arrays total, not n number of 3 element arrays.

    >> ("a".."z").to_a / 3
    =>
    [["a", "b", "c", "d", "e", "f", "g", "h", "i"],
     ["j", "k", "l", "m", "n", "o", "p", "q", "r"],
     ["s", "t", "u", "v", "w", "x", "y", "z"]]
    
    Brandon Brandon
    July 13, 2008 at 08:23 AM

Speak your mind:

(Required)

(Required)


(You may use textile in your comments.)

About

I'm Brandon Keepers, a web application developer that likes beautiful code, valid markup and adherence to standards. As a part of Collective Idea in Holland, Michigan, I practice Agile software development primarily using Ruby on Rails.

-86.103171 42.785037

Contact:

more ยป

Syndicate