How to build a Roman Numeral to Integer Function in JavaScript

Saul Feliz
The Startup
Published in
5 min readJun 20, 2020

--

The Romans could build, but boy were their numbers complicated!

A buddy of mine recently had a technical interview where he was asked to build a function that would take a Roman numeral, and return a regular integer most of us can read and understand. I thought that might be useful anytime reading the credits to movies filmed prior to 1990.

The thing about Roman numerals, though, is that they’re not all that intuitive. For example, if 1 = I, and 2 = II, and 3 = III, you might expect 4 to equal IIII. But of course it doesn’t. It’s IV. So I immediately thought, “oh…I’m not sure I know all these rules.” Luckily, the interviewer gave my friend the rules:

  • I can be placed before V (5) and X (10) to make 4 and 9.
  • X can be placed before L (50) and C (100) to make 40 and 90.
  • C can be placed before D (500) and M (1000) to make 400 and 900.

The function should accept a Roman numeral in the form of a string, and return the same value as an integer. To make things simpler, we’ll assume the input value will be between 1 and 3999. Let’s code this up!

A few things we need to keep into account are:

  1. We have to have access to something that translates what the roman character is in numbers. I’ve chosen to simply use an object for this.
  2. We have to take into account the Roman numeral rules somehow. I’ve chosen to simply use a bunch of conditionals. Some might not find this approach very elegant. Personally, I don’t mind listing conditionals. For me, thus far, the vast majority of time spent coding is thinking, not actually typing.

So let’s get to it. First we create an key::value pair object that we can then use to look up what each of these characters means in numbers:

const romanHash = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000,
};

Let’s assume the string (s) we get to translate to numbers is “MCMLXXXIX”. Let’s write our function, and immediately initialize a variable that will hold our result. I’m going to call this variable accumulator because that’s how I think of this algorithm. We essentially have to,

  1. Iterate through this string we’ve received
  2. As we iterate through characters accumulate the numbers we see and translate according to the rules we’ve received.

It’s easy to forget (I know I sometimes still do): strings are iterables in JavaScript. So no need to split or anything of the sort. Let’s walk through the rest of it:

  • At s[0] we’re iterating with “M”. A simple “M” doesn’t satisfy any of our conditions, so we’re immediately going to hit our else statement, look up the value of M in our romanHash object, see that its 1000, and add 1000 to our accumulator (line 36). So we now have a value of 1000 in the accumulator.
  • At s[1] we’re iterating with “C”. Notice there are two conditionals with C and one of them says that if the next value of the string (s[i + 1]) is M, we’re to add 900 to our accumulator (line 33). That’s our case, so now we have a value of 1900 in our accumulator.
  • The next line (line 34) is key, and I’d imagine what can trip most people up in this problem. Since we’ve already taken into account the character after the one we’re processing, we don’t want to look at it again. So we immediately increment our iterator.
  • Now we’re iterating with s[3] or L. L is not in any of our conditionals, so again, we hit our else statement (line 36), look up the value of L, and add it to our accumulator. We now have a value of 1950.
  • The same thing happens for the next three characters: X, X, and X. It doesn’t hit any of our conditions, so the line 36 evaluates to true every time, looks up the value of 10 in our romanHash object, and adds it to the accumulator. We now have 1980 as an accumulator value.
  • We’re now iterating with s[7], or I. We see in line 20 that we have a condition that if we’re iterating with I, and the next value is X, we are to add 9 to our accumulator and increment our iterator. We now have 1989 as our accumulator value.
  • Because we incremented our iterator to 8, in the next loop, i = 9, which is in fact the length of our iterable object, so we break the loop, and return our accumulator value: 1989.

Here’s the full code in copy/paste format if you need it:

const romanHash = {
I: 1,
V: 5,
X: 10,
L: 50,
C: 100,
D: 500,
M: 1000,
};
const s = "MCMLXXXIX";
// s = 1989
function romanToInt(s) {
let accumulator = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] === "I" && s[i + 1] === "V") {
accumulator += 4;
i++;
} else if (s[i] === "I" && s[i + 1] === "X") {
accumulator += 9;
i++;
} else if (s[i] === "X" && s[i + 1] === "L") {
accumulator += 40;
i++;
} else if (s[i] === "X" && s[i + 1] === "C") {
accumulator += 90;
i++;
} else if (s[i] === "C" && s[i + 1] === "D") {
accumulator += 400;
i++;
} else if (s[i] === "C" && s[i + 1] === "M") {
accumulator += 900;
i++;
} else {
accumulator += romanHash[s[i]];
}
}
return accumulator;
}

Thanks for reading!

And remember: grit > talent.

--

--