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, ' ') {|group| p group}
["1", "2"]
["3", "4"]
["5", " "]
%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.