JavaScript Roman Numeral Converter

This simple tool, written in JavaScript, will convert Roman numerals to an integer and integers to Roman numerals.

In order to handle very large numbers, the tool uses the conversion chart below and is therefore case-sensitive. The lowercase numerals are traditionally represented capitalized and overlined.

Conversion Chart

I: 1
V: 5           v: 5,000
X: 10          x: 10,000
L: 50          l: 50,000
C: 100         c: 100,000
D: 500         d: 500,000
M: 1,000       m: 1,000,000

The Code Download

var romanToInt = function (numeral) {
    "use strict";
    const romanNumerals = {
        "I": 1,
        "V": 5,
        "X": 10,
        "L": 50,
        "C": 100,
        "D": 500,
        "M": 1000,
        "v": 5000,
        "x": 10000,
        "l": 50000,
        "c": 100000,
        "d": 500000,
        "m": 1000000
    };

    const romanNumeralsForConversion = {
        "I": 1,
        "IV": 4,
        "V": 5,
        "IX": 9,
        "X": 10,
        "XL": 40,
        "L": 50,
        "XC": 90,
        "C": 100,
        "CD": 400,
        "D": 500,
        "CM": 900,
        "M": 1000,
        "Mv": 4000,
        "v": 5000,
        "Mx": 9000,
        "x": 10000,
        "xl": 40000,
        "l": 50000,
        "xc": 90000,
        "c": 100000,
        "cd": 400000,
        "d": 500000,
        "cm": 900000,
        "m": 1000000
    };

    /**
     * Takes a string and returns true if each character is
     * a valid roman numeral
     * @param {String} val
     * @returns {Boolean}
     */
    function isValidNumeral(val) {
        var i = 0;
        for (i; i < val.length; i += 1) {
            if (romanNumerals[val.charAt(i)] === undefined) {
                return false;
            }
        }
        return true;
    }

    /**
     * Takes a string containing only valid roman numerals
     * and returns its arabic numeral equivalent
     * @param {String} val
     * @returns {Number}
     */
    function calculateIntValue(val) {
        var result = 0,
            c = 0,
            cm = 0,
            i = (val.length - 1);
        for (i; i >= 0; i -= 1) {
            c = parseInt(romanNumerals[val.charAt(i)], 10);
            cm = parseInt(romanNumerals[val.charAt(i - 1)], 10);
            if (isNaN(cm) || cm >= c) {
                result += c;
            } else {
                result += c - cm;
                i -= 1;
            }
        }
        return result;
    }

    /**
     * Takes an integer and returns its roman numeral equivalent.
     * @param {Number} v
     * @returns {String}
     */
    function calculateRomanVal(v) {
        var str = '',
            numeralKeys = Object.keys(romanNumeralsForConversion),
            maxVal = numeralKeys[numeralKeys.length - 1], // largest Roman Numeral
            cr = maxVal,
            r;
        while (v > 0) {
            for (r in romanNumeralsForConversion) {
                if (romanNumeralsForConversion.hasOwnProperty(r)) {
                    if (romanNumeralsForConversion[r] > v || r === maxVal) {
                        if (r === maxVal && v >= romanNumeralsForConversion[r]) {
                            // The number is larger than or exactly equal to the largest Roman numeral
                            v = v - romanNumeralsForConversion[r];
                            str = (str + r);
                        } else {
                            // The number is smaller than the current numeral
                            v = v - romanNumeralsForConversion[cr];
                            str = (str + cr);
                        }
                        break;
                    }
                    cr = r;
                }
            }
        }
        return str;
    }

    (function main() {
        var toHTML = 'roman-result', // class of element to write result to
            val = numeral,
            result = '',
            noNumberErrorMsg = 'Enter a valid Roman Numeral or integer above',
            invalidNumberErrorMsg = 'Invalid Roman Numeral or integer',
            tooLargeNumberErrorMsg = 'Try a smaller number. You\'re just going to get a lot of "m"s...',
            results;
        if (val.length === 0) {
            result = noNumberErrorMsg;
        } else if (isValidNumeral(val)) {
            result = '<div class="roman-result-number">' + calculateIntValue(val) + '</div>' + val + ' Expressed in Arabic Numerals';
        } else if (parseInt(val, 10) == val) { // soft equals to compare int val with string
            if(val > 9999999) {
                result = tooLargeNumberErrorMsg;
            } else {
                result = '<div class="roman-result-number"">' + calculateRomanVal(val) + '</div>' + val + ' Expressed in Roman Numerals';
            }
        } else {
            result = invalidNumberErrorMsg;
        }
        results = document.getElementsByClassName(toHTML);
        for(var i = 0; i < results.length; i++) {
            results[i].innerHTML = result;
        }
    }());
};

© 2006-2020 quixfox at gmail dot com some rights reserved