If Else

Roman Numerals
Roman Numerals in Python
def roman(number):
    # The notation: I, V, X, L, C, D, M = 1, 5, 10, 50, 100, 500, 1000
    m = number // 1000
    m_rem = number % 1000
    c = m_rem // 100
    c_rem = m_rem % 100
    x = c_rem // 10
    x_rem = c_rem % 10
    i = x_rem

    res = ''

    if m > 0:
        res +=  m * 'M'
    
    if 4 > c > 0:
        res +=  c * 'C'
    elif c == 4:
        res +=  'CD'
    elif 9 > c > 4:
        res +=  'D' + ((c - 5) * 'C')
    elif c == 9:
        res +=  'CM'

    if 4 > x >  0:
        res += x * 'X'
    elif x == 4:
        res += 'XL'
    elif 9 > x > 4:
        res += 'L' + ((x - 5) * 'X')
    elif x == 9:
        res +=  'XC'

    if 4 > i >  0:
        res += i * 'I'
    elif i == 4:
        res += 'IV'
    elif 9 > i > 4:
        res += 'V' + ((i - 5) * 'I')
    elif i == 9:
        res += 'IX'
    
    return res

This gets the job done. Something like it would work in most languages, though Python's range test (a > x > b) saves some boolean logic.

Refactoring

The code above is quite long and a bit repetitive. We should explore ways to make it more concise.

The first block is just a way to extract the digits from the input number. This can be done with a list comprehension, left-padding with zeros as necessary:

digits = ([0, 0, 0, 0] + [int(d) for d in str(number)])[-4:]

The blocks for hundreds, tens and units are all essentially the same, so we can put that code in a function. We just need to pass in the digit, plus a tuple of translations for (1, 4, 5, 9) or their 10x and 100x equivalents.

It is also unnecessary to keep retesting the lower bounds within an elif, as the code line will only be reached if that is satisfied.

Using return instead of elif is a matter of personal preference. Given that, the code simplifies to:

def roman(number: int) -> str:
    def translate_digit(digit: int, translations: iter) -> str:
        assert isinstance(digit, int) and 0 <= digit <= 9

        units, four, five, nine = translations
        if digit < 4:
            return digit * units
        if digit == 4:
            return four
        if digit < 9:
            return five + (digit - 5) * units
        return nine

    assert isinstance(number, int)
    m, c, x, i = ([0, 0, 0, 0] + [int(d) for d in str(number)])[-4:]
    res = ''

    if m > 0:
        res += m * 'M'
    if c > 0:
        res += translate_digit(c, ('C', 'CD', 'D', 'CM'))
    if x > 0:
        res += translate_digit(x, ('X', 'XL', 'L', 'XC'))
    if i > 0:
        res += translate_digit(i, ('I', 'IV', 'V', 'IX'))

    return res

The last few lines are quite similar and it would be possible to refactor them into a loop, but this is enough to illustrate the principle.

8th May 2024 · Found it useful?

Other Approaches to Roman Numerals in Python

Other ways our community solved this exercise