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

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

June 24, 2023

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.

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

Sign up for my newsletter

Let's learn to become better developers.

Comments (3)

D
Domhnall

Great article as always Akshay. Well done. I think there is some potential for confusion in your discussion of the `in_groups_of` method. The sample code actually demonstrates the behaviour of the, similarly-named but slightly different, method `in_groups`.

A
Akshay Khot

Thanks for pointing the mistake, Domhnall. You're right. I had copied the wrong example from the Rails API docs. I've updated the post. Thank you!

V
Viktor

Great article !

Sign in to leave a comment.