I have some embarrassingly long code which puts into words any number up into the trillions. As a newbie, and understanding that shorter, non-repetitive code is best, I am looking for suggestions on how to reduce this code to a respectable quantity. I realize that there is a lot of repetition in it. However, depending on the number being evaluated, the math looks a little different with each rep, so I am not sure if I can reduce that. I have tried rewriting it solely as an if/else (without the recursion) but it quickly becomes just as bad if not worse.
class :: Fixnum
def in_words(number)
if number < 0 # No negative numbers.
return 'Please enter a number that isn\'t negative.'
end
if number == 0
return 'zero'
end
numString = '' # This is the string we will return.
onesPlace = ['one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine']
tensPlace = ['ten', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety']
teenagers = ['eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen']
#-----------------------------------------trillions
left = number
write = left/1000000000000
left = left - (write*1000000000000)
if number > 999999999999
if write < 10 #1,00 - 9,000
millions = onesPlace[write - 1]
numString = numString + millions + ' trillion'
end
if (write > 9) && (write < 20) #10,000 - 19,000
if write == 10
millions = tensPlace[write - 10]
numString = numString + millions + ' trillion'
else
millions = teenagers[write - 11] #11 because the length of teenagers is only 9 so 15-11 = 4 and 'thirteen' is in
numString = numString + millions + ' trillion'
end
end
if (write > 19) && (write < 1000) #here i have to use recursion to get the first two/three digets --> 19,000,000 - 999,000,000
millions = in_words(write)
numString = numString + millions + ' trillion'
end
if left > 0
numString = numString + ' '
end
#-----------------------------------------billions
#left = number
write = left/1000000000
left = left - (write*1000000000)
if number > 999999999
if write < 10 #1,00 - 9,000
millions = onesPlace[write - 1]
numString = numString + millions + ' billion'
end
if (write > 9) && (write < 20) #10,000 - 19,000
if write == 10
millions = tensPlace[write - 10]
numString = numString + millions + ' billion'
else
millions = teenagers[write - 11] #11 because the length of teenagers is only 9 so 15-11 = 4 and 'thirteen' is in
numString = numString + millions + ' billion'
end
end
if (write > 19) && (write < 1000) #here i have to use recursion to get the first two/three digets --> 19,000,000 - 999,000,000
millions = in_words(write)
numString = numString + millions + ' billion'
end
if left > 0
numString = numString + ' '
end
# ----------------------------------------millions
write = left/1000000
left = left - (write*1000000)
if number > 999999
if write < 10 #1,00 - 9,000
millions = onesPlace[write - 1]
numString = numString + millions + ' million'
end
if (write > 9) && (write < 20) #10,000 - 19,000
if write == 10
millions = tensPlace[write - 10]
numString = numString + millions + ' million'
else
millions = teenagers[write - 11] #11 because the length of teenagers is only 9 so 15-11 = 4 and 'thirteen' is in
numString = numString + millions + ' million'
end
end
if (write > 19) && (write < 1000) #here i have to use recursion to get the first two/three digets --> 19,000,000 - 999,000,000
millions = in_words(write)
numString = numString + millions + ' million'
end
if left > 0
numString = numString + ' '
end
#-----------------------------------------thousands
write = left/1000
left = left - (write*1000)
if number > 999
if write < 10 #1,00 - 9,000
thousands = onesPlace[write - 1]
numString = numString + thousands + ' thousand'
end
if (write > 9) && (write < 20) #10,000 - 19,000
if write == 10
thousands = tensPlace[write - 10]
numString = numString + thousands + ' thousand'
else
thousands = teenagers[write - 11] #11 because the length of teenagers is only 9 so 15-11 = 4 and 'thirteen' is in
numString = numString + thousands + ' thousand'
end
end
if (write > 19) && (write < 1000) #here i have to use recursion to get the first two/three digits --> 19,000 - 999,000
thousands = in_words(write)
numString = numString + thousands + ' thousand'
end
if left > 0
numString = numString + ' '
end
end
end
end
end
# ------------- hundreds
write = left/100
left = left - (write*100)
if write > 0
hundreds = in_words(write)
numString = numString + hundreds + ' hundred'
if left > 0
numString = numString + ' '
end
end
# ---------------- tens
write = left/10 #stop here and return
#numString = numString +
left = left - write*10
if write > 0
if ((write == 1) and (left > 0))
numString = numString + teenagers[left-1]
left = 0
else
numString = numString + tensPlace[write-1]
end
if left > 0
numString = numString + '-'
end
end
write = left
left = 0
if write > 0
numString = numString + onesPlace[write-1]
end
numString
end
end#class
puts 95202824653012.in_words(95202824653012)
Another problem I have with it is that it is a method added to the Fixnum
class and I would like to be able to call it directly on self
without an argument, for example 3457278.in_words
instead of 3457278.in_words(3457278)
. The problem seems to be that the recursion needs an argument when it is called, and therefore the method needs one. Or is there a way around that?
-
1\$\begingroup\$ Related question \$\endgroup\$Flambino– Flambino2015年03月26日 00:21:56 +00:00Commented Mar 26, 2015 at 0:21
1 Answer 1
Rather than rewrite your whole solution, I'll point out more idiomatic ways to do this stuff in Ruby.
Recursion
You don't need an argument to do recursion, self
is enough.
class ::Fixnum
def in_words
# ...
millions = write.in_words
# ...
end
end
String Arrays
%w{}
is handy for making arrays containing one-word strings
ones_place = %{one two three four five six seven eight nine}
Long Numbers
_
can be used like a comma (or period, if you're European) in long numbers.
billion = 1_000_000_000
For powers of 10, you can also use exponentiation
trillion = 10 ** 12
Modulus
%
gets the remainder of a number after dividing.
write = left / 10 ** 9
left = left % 10 ** 9
Assignment Shortcut
x = x <op> y
can be written as x <op>= y
where <op>
is any operator.
numString += ' '
left %= 10 ** 9
Loops
Once you incorporate all of those, you should be able to see how that huge nested if
can be turned into a tiny
[12, 9, 6, 3].each do |power|
break if self < 10 ** power
# ...
end
-
\$\begingroup\$ awesome suggestions! in addition, because of the specific answer to the recursion I will mark this as the answer. I will use your suggestions...cant wait to see how much i can shorten this with them! \$\endgroup\$HolyMoly– HolyMoly2015年03月24日 22:38:43 +00:00Commented Mar 24, 2015 at 22:38
-
\$\begingroup\$ Actually, Code Review (where this got migrated) is not about rewriting code. This advice is fine. It is, however, the place for working code that needs to be refactored. \$\endgroup\$Mark Thomas– Mark Thomas2015年03月25日 20:51:11 +00:00Commented Mar 25, 2015 at 20:51