August 27, 2024

P95 - English number words.

On financial documents, like checks, numbers must sometimes be written in full words. For example, 175 will be written as one hundred seventy five. Write a function Int.toWords() to convert (non-negative) integer numbers to words.

kotlin

package org.kotlin99.misc

import org.junit.Assert.assertTrue
import org.junit.Test


fun Int.toWords(): String {
    fun String.postfix(other: String) = if (this == "zero") "" else this + other
    fun List<String>.joinNonEmpty() = filter(String::isNotEmpty).joinToString(" ")

    fun convertMillions(n: Int) = (n / 1000000).toWords().postfix(" million")
    fun convertThousands(n: Int) = (n / 1000).toWords().postfix(" thousand")
    fun convertHundreds(n: Int) = (n / 100).toWords().postfix(" hundred")
    fun convertTensAndOnes(n: Int): String {
        return when {
            n < 10      -> ones[n]
            n in 10..19 -> teens[n - 10]
            else        -> listOf(tens[n / 10], ones[n % 10]).joinNonEmpty()
        }
    }

    if (this == 0) return "zero"
    return listOf(
        convertMillions(this),
        convertThousands(this % 1000000),
        convertHundreds(this % 1000),
        convertTensAndOnes(this % 100)
    ).joinNonEmpty()
}

private val ones = listOf("", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
private val teens = listOf("ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen")
private val tens = listOf("", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety")


class P95Test {
    @Test fun `convert number to words`() {
        val actual = listOf(
            0, 1, 2, 7, 10, 11, 12, 13, 15, 19, 20, 21, 25, 29, 30, 35, 50, 55, 69, 70, 99,
            100, 101, 119, 510, 900, 1000, 5001, 5019, 5555, 10000, 11000, 100000, 123456, 199001, 1000000,
            1111111, 190000009
        ).map { it.toString() + " - " + it.toWords() }

        val expected = """
            |0 - zero
            |1 - one
            |2 - two
            |7 - seven
            |10 - ten
            |11 - eleven
            |12 - twelve
            |13 - thirteen
            |15 - fifteen
            |19 - nineteen
            |20 - twenty
            |21 - twenty one
            |25 - twenty five
            |29 - twenty nine
            |30 - thirty
            |35 - thirty five
            |50 - fifty
            |55 - fifty five
            |69 - sixty nine
            |70 - seventy
            |99 - ninety nine
            |100 - one hundred
            |101 - one hundred one
            |119 - one hundred nineteen
            |510 - five hundred ten
            |900 - nine hundred
            |1000 - one thousand
            |5001 - five thousand one
            |5019 - five thousand nineteen
            |5555 - five thousand five hundred fifty five
            |10000 - ten thousand
            |11000 - eleven thousand
            |100000 - one hundred thousand
            |123456 - one hundred twenty three thousand four hundred fifty six
            |199001 - one hundred ninety nine thousand one
            |1000000 - one million
            |1111111 - one million one hundred eleven thousand one hundred eleven
            |190000009 - one hundred ninety million nine
        """.trimMargin().trim().split("\n")

        val mismatches = actual.zip(expected).filter { it.first != it.second }
        mismatches.forEach(::println)
        assertTrue(mismatches.isEmpty())
    }
}
Be first to comment
Leave a reply