5
\$\begingroup\$

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?

200_success
145k22 gold badges190 silver badges478 bronze badges
asked Mar 24, 2015 at 21:20
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Related question \$\endgroup\$ Commented Mar 26, 2015 at 0:21

1 Answer 1

4
\$\begingroup\$

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
answered Mar 24, 2015 at 22:15
\$\endgroup\$
2
  • \$\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\$ Commented 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\$ Commented Mar 25, 2015 at 20:51

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.