I’m working on a personal project that involves calendars. One of the features I wanted was to be able to print a calendar like this:

| Mon | Tue | Wed | Thu | Fri | Sat | Sun |
|-----+-----+-----+-----+-----+-----+-----|
|   1 |   2 |   3 |   4 |   5 |   6 |   7 |
|   8 |   9 |  10 |  11 |  12 |  13 |  14 |
|  15 |  16 |  17 |  18 |  19 |  20 |  21 |
|  22 |  23 |  24 |  25 |  26 |  27 |  28 |

For that I needed to split a 28-ish Array into 4 or 5 rows (weeks). In order to make this happen I took the long way around:

(1..28).group_by { |n| (n-1) / 7}.values
# => [[1, 2, 3, 4, 5, 6, 7],
#     [8, 9, 10, 11, 12, 13, 14],
#     [15, 16, 17, 18, 19, 20, 21],
#     [22, 23, 24, 25, 26, 27, 28]]

This works, but it’s extremely ugly. So I went digging into the Array and Enumerable documentation and found the each_slice method in the second one. It accepts the expected number of elements per sub-array and returns an Enumerator. Converting it to an Array gives us the result we expected

weeks = (1..28).each_slice(7)

weeks
# => #<Enumerator: ...>

weeks.to_a
# => [[1, 2, 3, 4, 5, 6, 7],
#     [8, 9, 10, 11, 12, 13, 14],
#     [15, 16, 17, 18, 19, 20, 21],
#     [22, 23, 24, 25, 26, 27, 28]]

If the number of elements isn’t an exact multiple of the expected number of elements, the last sub-array becomes smaller than the rest.

weeks = (1..31).each_slice(7).to_a
# => [[1, 2, 3, 4, 5, 6, 7],
#     [8, 9, 10, 11, 12, 13, 14],
#     [15, 16, 17, 18, 19, 20, 21],
#     [22, 23, 24, 25, 26, 27, 28],
#     [29, 30, 31]]

By passing it a block, each_slice will iterate through each sub-array

(1..8).each_slice(3) do |arr|
  arr.each do |n|
    print "(#{n})"
  end
  print "\n"
end

# >> (1)(2)(3)
# >> (4)(5)(6)
# >> (7)(8)

Thanks for reading.

Saluti.