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?

\$\endgroup\$
1

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
\$\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\$
    – HolyMoly
    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\$ Mar 25, 2015 at 20:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.