The Number Narrator

A medium Python interview practice problem on DataDriven. Write the Python and run it against test cases, with instant feedback.

Domain
Python
Difficulty
medium
Seniority
L4
Asked in
Citadel

Problem

A support engineer keeps getting tickets saying the invoicing tool's read-aloud feature garbles amounts, and the fix is to voice each amount, a non-negative integer, as its standard English words. Return that word form, spelling 0 as Zero.

Every number has a story in words.

Example 1

Input:

num = 123

Output:

"One Hundred Twenty Three"

Example 2

Input:

num = 1000000

Output:

"One Million"

Worked solution and explanation

What this problem really is

Strip the costume and this is a base-1000 positional decode: chop the number into three-digit groups, name each group the exact same way, and staple a scale word on. Anyone can print the digits; the skill is surviving the two things that break a naive attempt. First, teens are irregular, so 13 is 'Thirteen', never 'Ten Three', which kills any digit-by-digit scheme and forces a lookup table for 10 through 19. Second, a zero-valued group produces no words at all, so 1000000 must not emit 'Zero Thousand' or leave a doubled space where the empty thousands group used to sit. Miss either and you ship 'One Million' trailed by a stray gap, or 'Ten Three' for a teen.


Break down the requirements

Step 1: Handle zero as a special case

Return 'Zero' immediately, since it does not follow the standard decomposition and would otherwise produce an empty string.

Step 2: Define word mappings for ones, teens, and tens

Store lookup tables for 1-19 (which absorb the irregular teens) and for the multiples of 10 from 20 to 90. Naming by arithmetic is where candidates trip.

Step 3: Process the number in groups of three digits

Peel off billions, millions, thousands, and the remainder in turn. Convert each three-digit group with the same helper, and append its scale label only when the group is non-empty.

Step 4: Convert a three-digit group to words

Handle the hundreds place first, then the last two digits, using the teens table (10-19) or tens plus ones. Collect only non-empty parts so the join stays clean.


The solution

Chunk-based conversion with lookup tables
def number_to_words(num):
    if num == 0:
        return 'Zero'
    ones = ['', 'One', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven',
            'Eight', 'Nine', 'Ten', 'Eleven', 'Twelve', 'Thirteen',
            'Fourteen', 'Fifteen', 'Sixteen', 'Seventeen', 'Eighteen', 'Nineteen']
    tens = ['', '', 'Twenty', 'Thirty', 'Forty', 'Fifty',
            'Sixty', 'Seventy', 'Eighty', 'Ninety']
    def convert_chunk(n):
        parts = []
        if n >= 100:
            parts.append(ones[n // 100])
            parts.append('Hundred')
            n = n % 100
        if n >= 20:
            parts.append(tens[n // 10])
            n = n % 10
        if n > 0:
            parts.append(ones[n])
        return ' '.join(parts)
    scales = [(1000000000, 'Billion'), (1000000, 'Million'), (1000, 'Thousand')]
    parts = []
    for divisor, label in scales:
        if num >= divisor:
            parts.append(convert_chunk(num // divisor))
            parts.append(label)
            num = num % divisor
    if num > 0:
        parts.append(convert_chunk(num))
    result = ' '.join(parts)
    return result

Common follow-up questions