rubyguides

Array#each_slice

.each_slice(n) and .each_cons(n) are Array methods that iterate over consecutive elements in groups of a specified size. They differ in how they create those groups.

The two methods are often used for batch processing, pagination, and sliding-window calculations. each_slice is the better choice when you want a fixed-size chunk that does not overlap with the next one. each_cons is the better choice when each group should share most of its elements with the next group. The examples below show both patterns because the overlap is easier to remember when you can see the groups change in real code. Once that shape is clear, the method names become a quick way to express the data flow.

each_slice

each_slice creates non-overlapping groups of n elements. Each element appears in exactly one slice.

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

numbers.each_slice(3) { |slice| p slice }
# Output:
# [1, 2, 3]
# [4, 5, 6]
# [7, 8]

When the array length is not evenly divisible by n, the last slice contains fewer elements.

The last slice gets whatever elements remain after filling the earlier full-sized groups, so a seven-element array sliced into threes produces groups of sizes 3, 3, and 1. This partial-final-group behavior avoids surprises in code that expects all slices to have the same size.

each_cons

each_cons creates sliding windows of n consecutive elements. Each window overlaps with the previous one.

numbers = [1, 2, 3, 4, 5, 6, 7, 8]

numbers.each_cons(3) { |window| p window }
# Output:
# [1, 2, 3]
# [2, 3, 4]
# [3, 4, 5]
# [4, 5, 6]
# [5, 6, 7]
# [6, 7, 8]

The each_cons output shows the overlapping nature of sliding windows: every element except the first and last appears in multiple groups. This is what makes it suitable for calculations that compare neighboring values, like detecting changes in a time series or computing moving averages.

Parameters

ParameterTypeDescription
nIntegerSize of each slice or window

Return Value

Both methods return an Enumerator if called without a block. This lets you chain other Enumerable methods.

# Using enumerator form
enum = [1, 2, 3, 4, 5, 6].each_slice(3)
enum.to_a  # => [[1, 2, 3], [4, 5, 6]]

# Chaining with map
result = [1, 2, 3, 4, 5, 6].each_slice(2).map(&:sum)
result  # => [3, 7, 11]

The enumerator form of both methods is what makes chaining possible — when no block is given, you get back an object that other Enumerable methods can consume. This is why each_slice(2).map(&:sum) works as a single pipeline rather than two separate steps.

Practical Examples

Pagination

items = (1..15).to_a
page_size = 5

items.each_slice(page_size).with_index(1) do |page, index|
  puts "Page #{index}: #{page.inspect}"
end
# Page 1: [1, 2, 3, 4, 5]
# Page 2: [6, 7, 8, 9, 10]
# Page 3: [11, 12, 13, 14, 15]

The pagination example uses each_slice with with_index to label each page as it is produced, which is a clean pattern for any code that needs to display or save results in numbered batches. The page size variable keeps grouping logic separate from display logic.

Running averages

prices = [10, 20, 30, 40, 50]

averages = prices.each_cons(2).map { |a, b| (a + b) / 2.0 }
# => [15.0, 25.0, 35.0, 45.0]

Running averages are a natural fit for each_cons because each calculation needs adjacent pairs. The sliding window gives you exactly the overlapping groups you need without index arithmetic or off-by-one risks — the method guarantees every consecutive pair reaches the block.

Processing matrix rows

matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9]
]

# Convert columns to rows
matrix.transpose.each_slice(2) { |pair| p pair }

The mistake section below shows the most common confusion between these methods. The difference is not about syntax but about whether elements can appear in multiple groups, and the code examples make that distinction concrete.

Common Mistakes

Forgetting that each_slice is non-overlapping while each_cons overlaps:

# Wrong: using each_cons for non-overlapping chunks
[1, 2, 3, 4].each_cons(2).to_a
# => [[1, 2], [2, 3], [3, 4]]  # 3 pairs, overlaps!

# Correct: each_slice for non-overlapping
[1, 2, 3, 4].each_slice(2).to_a
# => [[1, 2], [3, 4]]  # 2 chunks, no overlap

The mistake is easy to make because both method names sound similar and both group array elements by size. The crucial difference is whether the groups overlap: each_cons(2) produces three overlapping pairs while each_slice(2) produces two disjoint chunks from the same four-element input.

when to use which

  • each_slice: Paginating results, batch processing, creating groups
  • each_cons: Running calculations, sliding windows, comparing consecutive elements

If you are unsure which one to use, think about whether an item should appear in one group or many. That single question usually points to the right method and helps the code read like the business rule instead of like an implementation detail. If the next step needs a batch, use each_slice. If it needs overlap, use each_cons. That distinction keeps the loop code close to the real intent of the work.

See Also