Project Euler Problem 17 Solution

Question

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3+3+5+4+4=193 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?

NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of “and” when writing out numbers is in compliance with British usage.

JavaScript

const ones = {
  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'
}

const tens = {
  2: 'twenty',
  3: 'thirty',
  4: 'forty',
  5: 'fifty',
  6: 'sixty',
  7: 'seventy',
  8: 'eighty',
  9: 'ninety'
}

function english(number) {
  let parts = []

  if (number >= 1000) {
    parts.push(ones[Math.floor(number / 1000)])
    parts.push("thousand")
    number %= 1000
  }

  if (number >= 100) {
    parts.push(ones[Math.floor(number / 100)])
    parts.push("hundred")
    if (number % 100 !== 0) {
      parts.push("and")
    }
    number %= 100
  }

  if (number >= 20) {
    parts.push(tens[Math.floor(number / 10)])
    number %= 10
  }

  if (ones[number]) {
    parts.push(ones[number])
  }

  return parts.join("")
}

const words = []
for (let i = 1; i <= 1000; i++) {
  words.push(english(i))
}
console.log(words.join("").length)
$ time node --use-strict --harmony-destructuring english-numbers.js
real   0m0.081s
user   0m0.080s
sys    0m0.000s

Python

#!/usr/bin/env python
def to_english(number):
    _ones = {
            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 = {
            2: 'twenty',
            3: 'thirty',
            4: 'forty',
            5: 'fifty',
            6: 'sixty',
            7: 'seventy',
            8: 'eighty',
            9: 'ninety'
            }
    if abs(number) >= 10000:
        return str(number)
    elif number == 0:
        return 'zero'
    else:
        output = ''

        if number < 0:
            output += 'negative '
            number = abs(number)

        if number >= 1000:
            output += _ones[number // 1000]
            if number % 1000 == 0:
                output += " thousand"
            else:
                output += " thousand "
            number %= 1000

        if number >= 100:
            output += _ones[number // 100]
            if number % 100 == 0:
                output += " hundred"
            else:
                output += " hundred and "
            number %= 100

        if number >= 20:
            output += _tens[number // 10]
            number %= 10
            if number % 10 in _ones:
                output += '-'

        if number in _ones:
            output += _ones[number]

        return output

def cleanse_string(string):
    '''remove spaces and hyphens'''
    string = string.replace(' ', '')
    string = string.replace('-', '')
    return string

print(sum(len(cleanse_string(to_english(i))) for i in range(1, 1001)))
$ time python3 num-string-sum.py
real   0m0.023s
user   0m0.020s
sys    0m0.000s

Ruby

#!/usr/bin/env ruby
require 'linguistics' # gem install linguistics
Linguistics::use( :en )
puts (1..1000).map { |i| i.en.numwords.gsub(/[ -]/, '').length }.reduce(:+)
$ time ruby number-to-words.rb
real   0m0.495s
user   0m0.184s
sys    0m0.012s