say.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# Say a number in English (well, American actually)
class Say
  UNDER_TWENTY = %w(
    zero one two three four five six seven eight nine ten
    eleven twelve thirteen fourteen fifteen sixteen seventeen
    eighteen nineteen
  ).freeze

  TENS = %w(
    zero ten twenty thirty forty fifty sixty seventy eighty ninety
  ).freeze

  POWERS = {
    1_000_000_000 => 'billion',
    1_000_000 => 'million',
    1_000 => 'thousand',
    100 => 'hundred'
  }.freeze

  def initialize(number)
    raise ArgumentError, 'Negative number not allowed' unless number >= 0
    raise ArgumentError, 'Number is too large ' unless number < 1e12

    @number = number
  end

  def in_english
    Say.say(@number)
  end

  class << self
    # Say a number
    def say(number)
      if number < 20
        under_20(number)
      elsif number < 100
        under_100(number)
      else
        over_100(number)
      end
    end

    # Say a number under 20
    def under_20(number)
      UNDER_TWENTY[number]
    end

    # Say a number between 20 and 99
    def under_100(number)
      tens = TENS[number / 10]
      if number % 10 == 0
        # e.g. forty
        tens
      else
        # e.g. forty-two
        "#{tens}-#{say(number % 10)}"
      end
    end

    # Say a number 100 or over
    def over_100(number)
      power = POWERS.keys.select { |p| p <= number }.max
      name = POWERS[power]
      say_power = "#{say(number / power)} #{name}"
      remainder = number % power
      if remainder == 0
        # e.g. two hundred
        say_power
      else
        # e.g. two hundred twenty-three
        "#{say_power} #{say(remainder)}"
      end
    end
  end
end

Comments

Made lots of class level helper methods to avoid creating new objects when it recurses. Not 100% sure I am happy about it!

helenst commented 30 May 2016 at 21:01 UTC

You're not logged in right now. Please login via GitHub to comment