Enumerable#each_slice

Returns: Enumerator · Added in v1.8.7 · Updated March 31, 2026 · Enumerable
ruby enumerable each_slice iteration stdlib

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 first n elements, then the next n, with no overlap
  • each_cons(n) takes n consecutive elements, sliding forward by one each time (overlaps by n - 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