Roman numerals in JavaScript

Some years ago I developed a small Action Script 3 class to work with Roman numerals using regular expressions. As ActionScript is barely used on these days, I decided to port this class to JavaScript.

Although the Roman numerals are almost unused and are required just in specific contexts, I think that there will be always somebody that can find this useful.

This code is prepared to work with numbers until 3’999’999, so, any value greater than this will throw an execution error. For numbers greater than 3’999 a parenthesis notation has been used to indicate that the number is a multiple of 1’000.

Roman numerals in JavaScript

var RomanNumbers = function() {

	var _letters = ["I", "V", "X", "L", "C", "D", "M", "(V)", "(X)", "(L)", "(C)", "(D)", "(M)"];
	var _regexp = /^((\(M\)){0,3})(\(C\)\(M\)|\(C\)\(D\)|(\(D\))?(\(C\)){0,3})(\(X\)\(C\)|\(X\)\(L\)|(\(L\))?(\(X\)){0,3})(M\(X\)|M\(V\)|(\(V\))?)(M{0,3})(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/;

	//---Method to test a Roman numeral
	this.testRoman = function(roman) {

		return _regexp.test(roman);

	}

	//---Method to obtain a Roman numeral
	this.getRomanNumber = function(number) {

		if (number > 3999999) throw "Numbers higher than 3999999 can't be converted to Roman. Try a lower value!";

		var roman = "";
		var cant = String(number).length;
		var narray = String(number).split("").reverse();

		var parser = function(item, index, a) {
            
			switch(item) {

				case "0":
				case "1":
				case "2":
				case "3":
					roman = (new Array(Number(item) + 1).join(_letters[index * 2])) + roman;
				break;

				case "4":
					roman = _letters[index * 2] + _letters[index * 2 + 1] + roman;
				break;

				case "5":
				case "6":
				case "7":
				case "8":
					roman = _letters[index * 2 + 1] + (new Array(Number(item) - 4).join(_letters[index * 2])) + roman;
				break;

				case "9":
					roman = _letters[index * 2] + _letters[index * 2 + 2] + roman;
				break;

			}

		}

		narray.forEach(parser);

		return roman;

	}

	//---Method to obtain an Arabic number
	this.getArabicNumber = function (roman) {

		if (!this.testRoman(roman)) throw "You have entered an invalid Roman numeral. Please try with another value";

		var reg = /(\()(\w)(\))/g;
		var simple = "";
		var values = 0;
		var array = _regexp.exec(roman);

		array.splice(0, 1);
		array.splice(1, 1);
		array.splice(2, 2);
		array.splice(3, 2);
		array.splice(4, 1);

		var parser = function(item, index, a) {

			switch(index){

				case 0:
				case 1:
				case 2:
					simple = item.replace(reg, "$2");
					values += getValue(simple) * 1000;
				break;

				case 3:
					simple = item.replace(reg, "$2");
					values += ((simple.slice(0, 1) == "M") ? getValue(simple.slice(1, 2)) * 1000 - getValue(simple.slice(0, 1)) : getValue(simple) * 1000);
				break;

				case 4:
				case 5:
				case 6:
				case 7:
					values += getValue(item);
				break;

			}

		}

		array.forEach(parser);

		return values;

	}

	//---Private function to return a number from a Roman numeral string
	function getValue(str) {

		var cant = str.length;
		var chars = [];
		var ret = 0;

		switch(cant) {

			case 1:
				ret = getNumberByIndex(_letters.indexOf(str));
			break;

			case 2:
				chars = str.split("");
				ret = ((_letters.indexOf(chars[0]) < _letters.indexOf(chars[1])) ? getNumberByIndex(_letters.indexOf(chars[1])) - getNumberByIndex(_letters.indexOf(chars[0])) : getNumberByIndex(_letters.indexOf(chars[0])) + getNumberByIndex(_letters.indexOf(chars[1])));
			break;

			case 3:
				chars = str.split("");
				ret = sumAllNumbers(getNumberByIndex(_letters.indexOf(chars[0])), getNumberByIndex(_letters.indexOf(chars[1])), getNumberByIndex(_letters.indexOf(chars[2])));
			break;

			case 4:
				chars = str.split("");
				ret = sumAllNumbers(getNumberByIndex(_letters.indexOf(chars[0])), getNumberByIndex(_letters.indexOf(chars[1])), getNumberByIndex(_letters.indexOf(chars[2])), getNumberByIndex(_letters.indexOf(chars[3])));
			break;

		}

		return ret;

	}

	//---Private function to return a number taking into account the index in the letter Array
	function getNumberByIndex(index) {
		return ((index % 2 == 0) ? Math.pow(10, index / 2) : Math.pow(10, (index + 1) / 2) / 2);
	}

	//---Private function to sum multiple numbers
	function sumAllNumbers() {

		return Array.prototype.reduce.call(arguments, function (s, n) {
			return s + n;
		}, 0);

	}

}
Methods
// Method to return a Roman numeral from an Arabic number
getRomanNumber(int $number)
$number Int number to convert to Roman
// Method to return an Arabic number from a Roman numeral
getArabicNumber(string $roman)
$roman A String containing the Roman numeral to convert to Arabic. If a number greater than 1'000 is needed, it needs to be enclosed between parenthesis (e.g. (V) = 5'000)
// Method to check if a Roman numeral is valid
boolean testRoman(string $roman)
$roman A String containing the Roman numeral to test its validity

Code example

var manager = new RomanNumbers();
manager.testRoman("CCX"); // true
manager.testRoman("XXCC"); // false
manager.getRomanNumber(4529); // M(V)DXXIX
manager.getArabicNumber("M(X)IV"); // 9004

Demo of Roman numerals conversion in JavaScript

Demo

Download the example files

Download

(1 votes, average: 5.00 Out Of 5)
Share this post:

It is possible to insert code fragments between <pre></pre> tags and HTML or XML code between <xmp></xmp> tags.

Leave a Reply

Your email address will not be published. Required fields are marked *

Time limit is exhausted. Please reload CAPTCHA.