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.