michaelp0730
2/12/2015 - 9:09 PM

Get Roman Numeral

Pass a number to RomanNumerals.toRoman() to get its Roman Numerals equivalent. Pass a valid Roman Numeral string to RomanNumerals.fromRoman() to get its numeric value.

var RomanNumerals = {
    numeralsMap: {
        1: 'I',
        4: 'IV',
        5: 'V',
        9: 'IX',
        10: 'X',
        40: 'XL',
        50: 'L',
        90: 'XC',
        100: 'C',
        400: 'CD',
        500: 'D',
        900: 'CM',
        1000: 'M'
    },

    getObjectKeys: function (obj) {
        return Object.keys(obj);
    },

    getObjectValues: function (obj) {
        var values = [];
        for (var o in obj) {
            values.push(obj[o]);
        }
        return values;
    },

    getKeyByValue: function (obj, val) {
        for (var prop in obj) {
            if(obj.hasOwnProperty(prop) && obj[prop] === val) {
                return parseInt(prop, 10);
            }
        }
    },

    getLargestNumeral: function (num) {
        var numeralKeys = this.getObjectKeys(this.numeralsMap);
        var numeral = '';
        for (var i = 0; i < numeralKeys.length; i++) {
            if (parseInt(numeralKeys[i], 10) <= num) {
                numeral = numeralKeys[i];
                if (i === numeralKeys.length - 1) {
                    return numeral;
                }
            } else {
                return numeral;
            }
        }
    },

    isValidCharacter: function (char) {
        var validCharacters = ['I', 'V', 'X', 'L', 'C', 'D', 'M'];
        return validCharacters.indexOf(char) !== -1;
    },

    toRoman: function (num) {
        var numeralString = '';
        var numeralKeys = this.getObjectKeys(this.numeralsMap);
        var largestPossibleNumeral, tmp;
        if (numeralKeys.indexOf(num.toString()) !== -1) {
            return this.numeralsMap[num];
        } else {
            tmp = num;
            largestPossibleNumeral = this.getLargestNumeral(tmp);
            while (tmp > 0) {
                tmp = tmp - largestPossibleNumeral;
                numeralString += this.numeralsMap[largestPossibleNumeral];
                if (largestPossibleNumeral > 1) {
                    largestPossibleNumeral = this.getLargestNumeral(tmp);
                }
            }
            return numeralString;
        }
    },

    fromRoman: function (str) {
        var numeralValues = this.getObjectValues(this.numeralsMap);
        var validStr = true;
        var num = 0;
        var counter = 1;
        var tmp, newStr;

        for (var i = 0; i < str.length; i++) {
            if (!this.isValidCharacter(str[i])) {
                validStr = false;
            }
        }

        if (validStr) {
            if (numeralValues.indexOf(str) !== -1) {
                return this.getKeyByValue(this.numeralsMap, str);
            } else {
                newStr = str;
                while (newStr.length > 0) {
                    tmp = newStr.slice(0, counter);
                    if (numeralValues.indexOf(tmp) !== -1) {
                        counter += 1;
                        if (counter > 3) { // end of string
                            num = num + this.getKeyByValue(this.numeralsMap, tmp);
                            newStr = '';
                        }
                    } else {
                        counter -= 1;
                        tmp = newStr.slice(0, counter);
                        num = num + this.getKeyByValue(this.numeralsMap, tmp);
                        newStr = newStr.slice(counter);
                        counter = 1;
                    }
                }
                return num;
            }
        } else {
            console.error('The string "' + str + '" is not a valid Roman Numeral');
        }
    }
};