Published on

Roman Numerals Conversion

Authors
  • Name
    Humam Fauzi
    Twitter

Roman numerals, like its arabian counter part, have the largest number on the left hand side and the lowest on the right hand side. However roman numerals have caveat which it sometimes implies subtraction. For example the roman numerals for number 4 is IV which is 5 (represented as V) subtracted by 1 (represented as I). This also happen in number 9 which represented as IX.

How we construct a program that takes string of valid roman numerals e.g. III, IV, XIV to its arabic numerals.

We can begin by creating an empty function

roman.go
    package roman

    func romanNumerals(roman string) int {
        return 0
    }

or in python we can write

roman.py
    def roman_numerals(roman :string) int:
        return 0

One of the approach we can take is creating a Map between roman numerals to its arabic counter part. You can choose to use Rune data type but for now I use regular string

roman.go
    var romanMap = map[string]int{
        "I": 1,
        "X": 10,
    }

for python, we can use built in dictionary data type to map roman numerals to arabic numerals.

roman.py
    roman_map = {
        "I": 1,
        "X": 10,
    }

The first instinct we have is to iterate the string from first to end. In each of its character, we translate it to arabic numerals and sum it to get the final number.

roman.go
    package roman

    var romanMap = map[string]int{
        "I": 1,
        "X": 10,
    }

    func romanNumerals(roman string) int {
        final := 0
        for _, value := roman {
            final += romanMap[string(value)]
        }
        return final
    }
roman.go
    roman_map = {
        "I": 1,
        "X": 10,
    }
    def roman_numerals(roman :string) int:
        final = 0
        for char in roman:
            final += roman_map[char]
        return final

It is a straight answer but we need to handle the subtraction part. If we plug IX to the program, it wont return the right answer; it would return 11 instead of 9. So how we handle the subtraction part?

There subtraction require additional knowledge that a regular for loop does not have. It require to know whether the next character arabic representation is larger than current one. If the next character is larger, then we the current one is a minus to the sum.

In programming languange, there is risk that we access the index that larger than the array length itself; an out of bound error. Since we want to access the next char, we need to know whether at the end or not to avoid out of bond.

roman.go
    package roman

    var romanMap = map[string]int{
        "I": 1,
        "X": 10,
    }

    func romanNumerals(roman string) int {
        final := 0
        totalLength := len(roman)
        for index, value := roman {
            next := index + 1
            num := romanMap[string(value)]
            if next == len(roman) {
                final += num
                break
            }
            nextNum := romanMap[string(roman[next])]
            if num > nextNum {
                final += num
            } else {
                final -= num
            }
        }
        return final
    }

To handle next array, there are two additional ifs, first if to handle if we at the last digit and we need to avoid out of bond error. The next if is the one handle if the current character is larger that next one. We translate the current character to num with romanMap[string(value)] and next character to num with romanMap[string(roman[next])]. We can do this in python

roman.go

    roman_map = {
        "I": 1,
        "X": 10,
    }
    def roman_numerals(roman :string) int:
        final = 0
        for (index, char) in enumerate(roman):
            next_index = index + 1
            num := roman_map[char]
            if next_index == len(roman):
                final += num
                break
            next_num := romanMap[roman[next_index]]
            if num > next_num:
                final += num
            else:
                final -= num
        return final