Viola Crellin - 4 years ago 118
Ruby Question

# `Array#each_slice`, leaving remainders at the beginning

I'm trying to slice an array into groups of three. I want to have the remainders at the beginning (

`[1, 2]`
in the following example).

``````arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
...
#=> [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
``````

Is there a nifty way to do this?

The usual way to split an array would be:

``````arr.each_slice(3)
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11]]
``````

This gives the remainders
`[10, 11]`
at the end. I tried as below, thinking
`each_slice`
might accept a negative argument and read it as going backwards through the array.

``````arr.each_slice(-3)
``````

Alas, it didn't work.

Though one might reverse an array thrice, there is more efficient way to achieve a goal:

``````arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
a = arr.dup # not to modify it inplace
[a.shift(a.size % 3)] + a.each_slice(3).to_a
#⇒ [[1, 2], [3, 4, 5], [6, 7, 8], [9, 10, 11]]
``````

BTW, `arr.each_slice(3)` returns an enumerator, not an array as you posted in the question.

upd or, as suggested by Cary Swoveland, to void `dup`:

``````n = a.size % 3
[a[0...n]] + a[n..-1].each_slice(3).to_a
``````

upd getting rid of `dup` by @sawa:

``````[a.first(a.size % 3)] + a.drop(a.size % 3).each_slice(3).to_a
``````

upd just out of curiosity (assuming an input has no `nil` elements):

``````([nil] * (3 - a.size % 3) + a).each_slice(3).to_a.tap do |a|
a.unshift(a.shift.compact!)
end
``````

the above might be safely run on original array, it does not modify it inplace.

UPD2 as pointed out by Stefan in comments, any of the above will produce an initial empty slice if the array is divisible by 3. So, the proper solution (and, the fastest, btw) should look like:

``````(arr.size % 3).zero? ? arr.each_slice(3).to_a : ANY_OF_THE_ABOVE
``````
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download