Enumerable#each_slice
Enumerator · Added in v1.8.7 · Updated March 31, 2026 · Enumerable The each_slice method takes elements from a collection and groups them into slices of a fixed size, yielding each complete slice one at a time. It is the natural choice when you need to process items in batches or divide a large collection into equal-sized chunks.
How It Works
collection.each_slice(n) { |group| block }
each_slice(n) collects n consecutive elements into an array and passes that array to the block. After the block finishes, it moves on to the next n elements. The final slice may contain fewer than n elements if the collection size is not evenly divisible by n — that partial slice is still yielded.
Basic Usage
Group an array into pairs:
[1, 2, 3, 4, 5].each_slice(2) { |pair| p pair }
# => [1, 2]
# => [3, 4]
# => [5]
Tripes:
%w[a b c d e f g].each_slice(3) { |chunk| p chunk }
# => ["a", "b", "c"]
# => ["d", "e", "f"]
# => ["g"]
Without a block, each_slice returns an Enumerator so you can chain other Enumerable methods:
enum = [1, 2, 3, 4, 5, 6].each_slice(2)
enum.map(&:sum)
# => [3, 7, 11]
Batch Processing
each_slice is a clean fit when you need to send items to an API in batches:
users = (1..50).to_a
users.each_slice(10) do |batch|
puts "Processing batch: #{batch.inspect}"
end
# Processing batch: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Processing batch: [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
# Processing batch: [21, 22, 23, 24, 25, 26, 27, 28, 29, 30]
# Processing batch: [31, 32, 33, 34, 35, 36, 37, 38, 39, 40]
# Processing batch: [41, 42, 43, 44, 45, 46, 47, 48, 49, 50]
Notice the last batch has all 10 elements — the collection divides evenly here. When the division is not even, the final slice is still yielded with whatever elements remain.
Pagination
Divide records into pages for display:
items = (1..23).to_a
page_size = 5
items.each_slice(page_size).with_index(1) do |page, number|
puts "Page #{number}: #{page.inspect}"
end
# Page 1: [1, 2, 3, 4, 5]
# Page 2: [6, 7, 8, 9, 10]
# Page 3: [11, 12, 13, 14, 15]
# Page 4: [16, 17, 18, 19, 20]
# Page 5: [21, 22, 23]
Grouped Output
Display a list in formatted columns:
names = %w[Alice Bob Carol Dave Eve Frank Grace Henry Ida Jasper Kate Leo Mia Noah Oliver]
col_size = 3
names.each_slice(col_size) do |row|
puts row.join(", ")
end
# Alice, Bob, Carol
# Dave, Eve, Frank
# Grace, Henry, Ida
# Jasper, Kate, Leo
# Mia, Noah, Oliver
each_slice vs each_cons
Both methods group elements, but the grouping behaviour differs:
each_slice(n)takes the firstnelements, then the nextn, with no overlapeach_cons(n)takesnconsecutive elements, sliding forward by one each time (overlaps byn - 1)
# each_slice — no overlap
[1, 2, 3, 4, 5].each_slice(3) { |s| p s }
# => [1, 2, 3]
# => [4, 5]
# each_cons — sliding window with overlap
[1, 2, 3, 4, 5].each_cons(3) { |s| p s }
# => [1, 2, 3]
# => [2, 3, 4]
# => [3, 4, 5]
Use each_slice when you want disjoint chunks. Use each_cons when you need a sliding window where consecutive groups share elements.
Incomplete Final Slices
each_slice does not discard a partial final slice. If the last slice has fewer than n elements, it is still yielded to the block. This is important to keep in mind when processing external resources where you might need a minimum batch size:
# Safe — partial slice is yielded
[1, 2, 3].each_slice(4) { |s| p s }
# => [1, 2, 3]
# If you need all slices to be exactly n elements long, filter:
[1, 2, 3].each_slice(4).select { |s| s.size == 4 }
# => []
See Also
- /reference/enumerable/enumerable-each-cons/ — sliding window iteration with overlapping groups
- /reference/enumerable/enumerable-tally/ — count occurrences of elements
- /guides/ruby-working-with-arrays/ — common array operations and patterns