Feature #18762
openAdd an Array#undigits that compliments Integer#digits
Description
I've found Integer#digits convenient and useful but several times have needed to go from the place-value notation back to an Integer after manipulation and wished there was a complimentary Array#undigits.
class Array
def undigits(base = 10)
each_with_index.sum do |digit, exponent|
digit * base**exponent
end
end
end
42.digits.undigits
#=> 42
42.digits(16).undigits(16)
#=> 42
Below is my stab at a Ruby implementation with behavior mirroring Integer#digits.
class Array
def undigits(base = 10)
base_int = base.to_int
raise TypeError, "wrong argument type #{base_int.class} (expected Integer)" unless base_int.is_a?(Integer)
raise ArgumentError, 'negative radix' if base_int.negative?
raise ArgumentError, "invalid radix #{base_int}" if base_int < 2
each_with_index.sum do |digit, exponent|
raise MathDomainError, 'out of domain' if digit.negative?
digit * base_int**exponent
end
end
end
Updated by jeremyevans0 (Jeremy Evans) about 1 year ago
- Tracker changed from Bug to Feature
- Backport deleted (
2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN)
Updated by sawa (Tsuyoshi Sawada) about 1 year ago
Using a chain of two methods each_with_index
and sum
as well as **
for such a simple task is an overkill, and perhaps that is why you feel you want a built-in method. You can simply do:
[2, 4].inject{_1 + _2 * 10} # => 42
Updated by shan (Shannon Skipper) about 1 year ago
sawa (Tsuyoshi Sawada) wrote in #note-3:
Using a chain of two methods
each_with_index
andsum
as well as**
for such a simple task is an overkill, and perhaps that is why you feel you want a built-in method. You can simply do:[2, 4].inject(0){_1 + _2 * 10} # => 42
@sawa (Tsuyoshi Sawada) The code you showed would sum the numbers multiplied by 10, resulting in 60
rather than 42
.
[2, 4].inject(0){_1 + _2 * 10} #=> 60
Updated by sawa (Tsuyoshi Sawada) about 1 year ago
@shan (Shannon Skipper) (Shannon Skipper)
Sorry. I had the code wrong. A similar code works for an array in backwards.
[].inject(0){_1 * 10 + _2} # => 0
[2, 1].inject(0){_1 * 10 + _2} # => 21
[3, 2, 1].inject(0){_1 * 10 + _2} # => 321
[6, 3, 2, 1].inject(0){_1 * 10 + _2} # => 6321
Updated by shan (Shannon Skipper) about 1 year ago
sawa (Tsuyoshi Sawada) wrote in #note-5:
@shan (Shannon Skipper) (Shannon Skipper)
Sorry. I had the code wrong. A similar code works for an array in backwards.
[].inject(0){_1 * 10 + _2} # => 0 [2, 1].inject(0){_1 * 10 + _2} # => 21 [3, 2, 1].inject(0){_1 * 10 + _2} # => 321 [6, 3, 2, 1].inject(0){_1 * 10 + _2} # => 6321
I think you'd just need to reverse it, since this result seems backwards. I think it's easy to get the implementation wrong and a handy method to have.
6321.digits.inject(0) { _1 * 10 + _2 } #=> 1234
6321.digits.reverse.inject(0) { _1 * 10 + _2 } #=> 4321
Using your #reduce strategy, you'd end up with something like the following.
class Array
def undigits(base = 10)
reverse.reduce(0) do |acc, digit|
acc * base + digit
end
end
end
It's a fairly similar shape to what I had.
class Array
def undigits(base = 10)
each_with_index.sum do |digit, exponent|
digit * base**exponent
end
end
end
I assumed we'd want to implement it in C if it's accepted, and was just trying to illustrate behavior, but unsure.
Updated by shan (Shannon Skipper) about 1 year ago
@sawa (Tsuyoshi Sawada) If this feature is accepted and for whatever reason a Ruby version is used instead of C, yours is more performant Ruby code than mine. It might look like the following. We'd want to implement this in C like Integer#digits, right?
class Array
def undigits(base = 10)
base_int = base.to_int
raise TypeError, "wrong argument type #{base_int.class} (expected Integer)" unless base_int.is_a?(Integer)
raise ArgumentError, 'negative radix' if base_int.negative?
raise ArgumentError, "invalid radix #{base_int}" if base_int < 2
reverse.reduce(0) do |acc, digit|
raise MathDomainError, 'out of domain' if digit.negative?
acc * base + digit
end
end
end