Stuck on algorithm. words to nums

93 views Asked by At

I'm trying to figure out a homework solution in Ruby. I want the answer obviously but you feel it defeats the purpose if I post it here and someone tells me 'this is how you do it'

I am trying to translate fixnum into English word pronunciations, up until the trillionth sizes. So it handles everything from 0 to 999_999_999_999_999

At first I thought of stacking the string as a FILO queue and simply .pop my way through it with a position 'count' to indicate what 'hundred' or 'thousand ' I am currently to spell out. The words, and denomination are set to class instance global hash values to call and verify as a key.

I'm not sure if it's a stupid question but I'm looking for a way to help me think to understand this small problem.

here's my spaghetti code. an improvement to the orignal of 95+ lines.

class Fixnum

  @@one_to_teen = {0 => "", 1 => 'one', 2 => "two", 3 => 'three', 4 => "four", 5 => 'five', 6 => 'six', 7=> 'seven', 8 => "eight", 9 => 'nine', 10 => 'ten', 11 => "eleven", 12 => 'twelve', 13 => 'thirteen', 14 => "fourteen", 15 => "fifteen", 16 => 'sixteen', 17 => "seventeen", 18 => "eighteen", 19 => "nineteen"}
  @@tens = {20 => "twenty", 30 => 'thirty', 40 => 'forty', 50 => 'fifty', 60 => 'sixty', 70 => 'seventy', 80 => 'eighty', 90 => 'ninety'}
  @@count_flag = {3 => "hundred", 4 => "thousand", 9 => "million", 12 => "billion", 15 => "trillion"}
  def in_words
      return "zero" if self == 0
      return @@one_to_teen[self] if self < 20
      #stack up
      num_stack = self.to_s.split('')
      count = 0
      temp_str = ""
      in_words_str = ""
      final_str = ""
    
      #loop until empty
      #i was trying to see if resetting the temp_str after being handle in the if statements helped get rid of the stack being used. not originally part of the logic.
    
      while !num_stack.empty?
          #puts num_stack.inspect
          temp_str << (num_stack.pop unless num_stack.empty?)
          count+=1
          if count%4 == 0
              in_words_str = "#{@@one_to_teen["#{temp_str[temp_str.length-1]}".to_i]} #{@@count_flag[count]} "
              temp_str = ""
          elsif count%3 == 0
              if temp_str[temp_str.length-1] != "0"
                  in_words_str = "#{@@one_to_teen["#{temp_str[temp_str.length-1]}".to_i]} #{@@count_flag[count]} "
                  #puts temp_str
              end
          elsif count%2 == 0
              in_words_str = "#{@@tens[("#{temp_str[1]}0").to_i]} #{@@one_to_teen[check_teens(temp_str).to_i]}"
              temp_str = ""
          end
          final_str = in_words_str + final_str
      end
      #somewhere in my logic i needed to do this, i was getting double spaces due to concat somewhere. bandaided it for now...
      return final_str.strip.gsub("  "," ")
  end
  def check_teens(temp_str)
      #swapping here to get correct "20" or higher wording.
      if temp_str.reverse.to_i < 20
          #puts temp_str.reverse
          return temp_str.reverse
      else
          return temp_str[0]
      end
  end
end
1

There are 1 answers

2
Beartech On

If you look at how you say 346,422,378 it's three hundred and forty six million four hundred and twenty two thousand three hundred and seventy eight.

You already have the right idea of mapping the groups of three to ones, tens and hundreds. Those then just get repeated.

So the algorithm might go something like:

Use a hash to map word values to each digits in the ones, tens, and hundreds.
Create another hash that contains "trillion", "billion", etc. position map
Get the number and remove any commas, or other formatting.
Is it zero? Easy, done.
Def a sub_string method which takes a group of three and divides that
  into three, using your ones, tens hundred mappings. It could return
  and array of the word string for each group.
Def a super_string method that then steps through the array you created
  and inserts the appropriate word ('trillion', 'million', etc.) between
  the array elements. 
  return your array using something like arr.join(' ') to get one string.

Here's what I would keep: edited to comply with original spec

class Fixnum

  ONES = {0 => "", 1 => 'one', 2 => "two", 3 => 'three', 4 => "four", 5 => 'five', 6 => 'six', 7=> 'seven', 8 => "eight", 9 => 'nine', 10 => 'ten', 11 => "eleven", 12 => 'twelve', 13 => 'thirteen', 14 => "fourteen", 15 => "fifteen", 16 => 'sixteen', 17 => "seventeen", 18 => "eighteen", 19 => "nineteen"}
  TENS = {20 => "twenty", 30 => 'thirty', 40 => 'forty', 50 => 'fifty', 60 => 'sixty', 70 => 'seventy', 80 => 'eighty', 90 => 'ninety'}
  COUNTS = {3 => "hundred", 4 => "thousand", 9 => "million", 12 => "billion", 15 => "trillion"}

  def self.in_words
    #fill in code here that uses the other two methods
  end

  def num_to_word_array(num)
    #chop up your number into the substrings and translate
  end

  def num_words_join(arr)
    #join your array using appropriate words, "billion", "million", etc
    #return the final string
  end

end

Try to divide it into steps that make sense as methods that can be chained. Post any changes you make and I'll be happy to critique.