4
\$\begingroup\$

So I wrote this function to convert a given number to its interpretation in the English language as part of the Project Euler exercises. It works fine, but I sense that it's rather sloppy and inelegant, especially for Python where many things can be done quickly in a couple of lines. Any feedback on how to make this code more beautiful/Pythonic is appreciated!

NUMBER_WORDS = {
    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",
    20 : "twenty",
    30 : "thirty",
    40 : "forty",
    50 : "fifty",
    60 : "sixty",
    70 : "seventy",
    80 : "eighty",
    90 : "ninety"
}

def convert_number_to_words(num):
    #Works up to 99,999
    num = str(num)
    analyze = 0
    postfix = remainder = None
    string = ""
    if len(num) > 4:
        analyze = int(num[0:2])
        remainder = num[2:]
        postfix = " thousand "
    elif len(num) > 3:
        analyze = int(num[0:1])
        remainder = num[1:]
        postfix = " thousand "
    elif len(num) > 2:
        analyze = int(num[0:1])
        remainder = num[1:]
        postfix = " hundred "
        if int(remainder) > 0:
            postfix += "and "
    elif int(num) in NUMBER_WORDS:
        analyze = int(num)
    else:
        analyze = int(num[0:1] + "0")
        remainder = num[1:]
        postfix = "-"
    string = NUMBER_WORDS[analyze]
    if postfix is not None:
        string += postfix
    if remainder is not None and int(remainder) > 0:
        return string + convert_number_to_words(remainder)
    else:
        return string
\$\endgroup\$
2
  • \$\begingroup\$ "And" is normally reserved for writing out fractions. 13,500 would be "thirteen thousand and five hundred" in your example, but would be said as "thirteen thousand five hundred". \$\endgroup\$
    – TyCobb
    Jan 13, 2014 at 21:48
  • \$\begingroup\$ @TyCobb Ah, good point. Editted. I'm with you that "and" shouldn't be included at all, but it's in the directions of the problem: projecteuler.net/problem=17 \$\endgroup\$
    – asteri
    Jan 13, 2014 at 21:55

1 Answer 1

4
\$\begingroup\$

Here's one using modulo % and list joining that uses your original NUMBER_WORDS dict:

def int_to_english(n):
    english_parts = []
    ones = n % 10
    tens = n % 100
    hundreds = math.floor(n / 100) % 10
    thousands = math.floor(n / 1000)

    if thousands:
        english_parts.append(int_to_english(thousands))
        english_parts.append('thousand')
        if not hundreds and tens:
            english_parts.append('and')
    if hundreds:
        english_parts.append(NUMBER_WORDS[hundreds])
        english_parts.append('hundred')
        if tens:
            english_parts.append('and')
    if tens:
        if tens < 20 or ones == 0:
            english_parts.append(NUMBER_WORDS[tens])
        else:
            english_parts.append(NUMBER_WORDS[tens - ones])
            english_parts.append(NUMBER_WORDS[ones])
    return ' '.join(english_parts)

It works up to 999,999, but could be extended further with a little customisation.

\$\endgroup\$
1
  • \$\begingroup\$ Your very last else shouldn't append 2 elements but insert them at once with a - as separation \$\endgroup\$
    – oliverpool
    Nov 27, 2015 at 14:04

Your Answer

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

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