split a list into n-equal parts in ruby and rails

How to Split a List into N Equal Parts in Ruby (and Rails)

This article shows you can split (or chunk) a list of items into n-equally sized parts using the `each_slice` method in Ruby and `in_groups_of` method in Rails.

2 min read

Earlier this week, I wanted to render a list of items on a website in a grid of 3 columns per row. Typically, I'd resort to Tailwind's Grid to accomplish this, but I wanted to do it programmatically in Ruby. While trying to hack a solution with loops and conditions, I came across the each_slice method in Ruby.

The each_slice method breaks the list into multiple, smaller lists of a given size. Since it returns self enumerator, you'd call to_a on it to get the chunked list.

items = [1, 2, 3, 4, 5, 6, 7]

chunks = items.each_slice(4).to_a

# [[1, 2, 3, 4], [5, 6, 7]]

Even better, since each_slice returns an enumerator, you can pass a block that operates on each chunk.

items = [1, 2, 3, 4, 5, 6, 7]

items.each_slice(4) { |chunk| pp chunk }

# [1, 2, 3, 4]
# [5, 6, 7]

This method is especially useful in views when working with a grid system. For example, imagine you have a list of ActiveRecord models you want to display in a grid. Here's how you'd arrange that list into three columns per row.

<% products.each_slice(3) do |chunk| %>
  <div class="row">
    <% chunk.each do |product| %>
      <div class="col-xs-4"><%= product.name %></div>
    <% end %>
  </div>
<% end %>

It will render the grid in a 3-column structure.

Are you using Rails?

Use the in_groups_of method.

in_groups_of(number, fill_with = nil, &block)

It splits or iterates over the array in groups of size number, padding any remaining slots with fill_with unless it is false.

%w(1 2 3 4 5 6 7 8 9 10).in_groups_of(3) {|group| p group}
["1", "2", "3"]
["4", "5", "6"]
["7", "8", "9"]
["10", nil, nil]

%w(1 2 3 4 5).in_groups_of(2, '&nbsp;') {|group| p group}
["1", "2"]
["3", "4"]
["5", "&nbsp;"]

%w(1 2 3 4 5).in_groups_of(2, false) {|group| p group}
["1", "2"]
["3", "4"]
["5"]

Another excellent use case for chunking is when you have a large number of items to process and you want to do it on a group of items at a time. For example, you have a list of a million users and you want to process them 1000 at a time.

users.in_groups_of(1000, false) do |group|
  # process the group of 1000 users
end

In fact, the in_groups_of method uses each_slice behind the scenes.

# active_support/core_ext/array/grouping.rb

def in_groups_of(number, fill_with = nil, &block)
  if fill_with == false
    collection = self
  else
    padding = (number - size % number) % number
    collection = dup.concat(Array.new(padding, fill_with))
  end

  if block_given?
    collection.each_slice(number, &block)
  else
    collection.each_slice(number).to_a
  end
end

I hope you liked this article and you learned something new.

As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.

If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.