Feature #12116
open`Fixnum#divmod`, `Bignum#divmod` with multiple arguments
Description
Sometimes, I need to apply divmod
repeatedly. For example, in order to convert a number expressing seconds into approx year, day, hour, minutes, seconds (approx in the sense of ignoring leap day and leap second), I can repeatedly apply divmod
:
seconds = 289342751
minutes, seconds = seconds.divmod(60) # => [4822379, 11]
hours, minutes = minutes.divmod(60) # => [80372, 59]
days, hours = hours.divmod(24) # => [3348, 20]
years, days = days.divmod(365) # => [9, 63]
so that I get that 289342751 seconds is approx 9 years 63 days 20 hours 59 minutes and 11 seconds. But it is cumbersome to do all that. It would be convenient if divmod
can take multiple arguments so that the conventional divmod
is applied from the right-most argument to the left, returning the above result at once:
289342751.divmod(365, 24, 60, 60) # => [9, 63, 20, 59, 11]
In general, when n
arguments are passed to the proposed divmod
, an array of n + 1
elements should be returned.
Another use case is nested arrays. Some people tend to express a matrix as a nested array, and try to access the innermost elements using multiple indices. To list the coordinates of the occurrences of 1
, one may do:
m = [
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]
a = []
m.each_with_index do |row, i|
row.each_with_index do |e, j|
a.push([i, j]) if e == 1
end
end
a # => [[0, 0], [1, 1], [2, 2]]
But it is often easier to have a flat array and use divmod
with it:
m = [
1, 0, 0,
0, 1, 0,
0, 0, 1,
]
m.each.with_index.select{|e, _| e == 1}.map{|_, i| i.divmod(3)} # => [[0, 0], [1, 1], [2, 2]]
However, once the nesting achieves another level, it becomes cumbersome. Instead of using a nested array:
t = [
[
["a", "b"],
["c", "d"],
],
[
["a", "b"],
["c", "d"],
],
]
one can keep using a flat array, but that would require repeated application of divmod
to covert between the flat index and the nested index. The proposed feature would also help in such case.