Roman Numeral Converter with JavaScript and underscore.js

This post demonstrates how to convert a number to a Roman numeral and vice versa with JavaScript. We’ve already covered how to convert a Roman numeral to an integer and how to convert an integer to a Roman numeral with Ruby. Check out the Roman numeral Wikipedia page to refresh your memory on this archaic number system.

Underscore.js

Underscore.js is an enormously helpful library that provides helper methods to give JavaScript some utility functions that are built-in to most other modern, high-level languages. Underscore helps prevent your code from deteriorating into a hopeless mess of for loops and counter variables.

Converting a Roman Numeral to a Number

A Roman numeral like “LXIV” can be converted to 64 by iterating through all the letters, converting each letter to a number, and summing the numbers. Numbers that are less than the subsequent number are treated as negative to account for irregularities in Roman numeral notation.

romanToNumber = function (roman) {
  var mapping = {
    "M": 1000,
    "D": 500,
    "C": 100,
    "L": 50,
    "X": 10,
    "V": 5,
    "I": 1
  }

  var arr = roman.split("");
  return _.inject(arr, function(memo, letter, i) {
    var num = mapping[letter];
    var next = mapping[arr[i + 1]];
    if (next === undefined || next <= num) {
      return memo + num;
    } else {
      return memo - num;
    }
  }, 0);
};

_.inject() is built into the underscore library. The first argument to inject is a collection and the second is a function that’s run for each iteration. The underscore code is much more readable for programmers coming from a language like Ruby.

Converting a Number to a Roman Numeral

Converting a number to a Roman numeral is a bit more tricky because some Roman numerals are irregular (e.g. 4 equals IV, not IIII). The number to Roman numeral mapping is updated to reflect the irregular Roman numerals.

The Roman numeral is constructed by iterating over every element in the mapping, performing integer division, appending letters to the result, and continuing the process with the remainder of the integer division (aka the modulus). For example, 3100 goes into 1000 three times with a remainder of 100. Three “M”s are appended to the result and the iteration continues with the remainder. 100 goes into 100 one time with a remainder of 0, so the final result if MMMC.

numberToRoman = function (number) {
  var mapping = {
    1000: "M",
    900: "CM",
    500: "D",
    400: "CD",
    100: "C",
    90: "XC",
    50: "L",
    40: "XL",
    10: "X",
    9: "IX",
    5: "V",
    4: "IV",
    1: "I"
  }

  var result = [];
  var keys = _.keys(mapping).reverse();
  _.each(keys, function(k) {
    var div = Math.floor(number / k);
    var mod = number % k;
    _(div).times(function() {
      result.push(mapping[k]);
    });
    number = mod;
  });
  return result.join("");
}

Leave a comment