Small util functions to validate Roman numerals, and to convert Roman numerals into the decimal numeral system and vice versa. Because of strict limitation of the Roman numeric system to represent the numerals, such as M, D, C, L, X, V, and I, the maximum roman numeral is “MMMCMXCIX” (3999). I have used recursion algorithm in the toDecimal function to make that function even smaller.

Upon checking several blogs and forums, I decided to create my own converter as the existing ones where either not readable, or they were buggy, or worse, they didn’t comply with the standard Roman numeric system (e.g. you could write the symbols such as M more than the standardized three times!). For the converter I created, I used React Bootstrap library as the frontend without any reason, just for fun.

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

export const MIN_DECIMAL_VALUE = 1;
export const MAX_DECIMAL_VALUE = 3999;
export const MIN_ROMAN_VALUE = toRoman(MIN_DECIMAL_VALUE);
export const MAX_ROMAN_VALUE = toRoman(MAX_DECIMAL_VALUE);

export function isRomanNumberValid(romanNumber) {
  return /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test(
    romanNumber
  );
}

export function isDecimalNumberValid(decimalNumber) {
  return (
    /^\d+$/.test(decimalNumber) &&
    decimalNumber >= MIN_DECIMAL_VALUE &&
    decimalNumber <= MAX_DECIMAL_VALUE
  );
}

export function toDecimal(romanNumber) {
  return toDecimalRecursion(romanNumber, 0);
}

export function toDecimalRecursion(romanNumber, decimalValue) {
  for (let key in lookup) {
    const value = lookup[key];
    if (romanNumber.indexOf(key) === 0) {
      decimalValue = value + toDecimalRecursion(romanNumber.substr(key.length), decimalValue);
      return decimalValue;
    }
  }
  return decimalValue;
}

export function toRoman(decimalValue) {
  let romanNumber = "";
  for (let i in lookup) {
    while (decimalValue >= lookup[i]) {
      romanNumber += i;
      decimalValue -= lookup[i];
    }
  }
  return romanNumber;
}

 

React with Bootstrap sample:

Here is the same algorithm for ExtJs:

Ext.define('App.util.RomanConvertor', {
    singleton: true,

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

    isRomanNumberValid: function(romanNumber) {
        return /^(?=[MDCLXVI])(M{0,3})(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$/.test(romanNumber);
    },

    toArabic: function (romanNumber) {
        if(!this.isRomanNumberValid(romanNumber)) {
            return "Invalid roman number";
        }
        return this.toArabicRecursion(romanNumber, 0);
    },

    toArabicRecursion: function (romanNumber, arabicValue) {
        Ext.Object.each(this.lookup, function (key, value) {
            if (romanNumber.indexOf(key) === 0) {
                arabicValue = value + this.toArabicRecursion(romanNumber.substr(key.length), arabicValue);
                return false;
            }
        }, this);
        return arabicValue;
    }
});

 

ExtJs sample: