# Roman Numeral Converter

## Problem Explanation

You will create a program that converts an integer to a Roman Numeral.

## Hints

### Hint 1

Creating two arrays, one with the Roman Numerals and one with the decimal equivalent for the new forms will be very helpful.

### Hint 2

If you add the numbers to the arrays that go before the new letter is introduced, like values for 4, 9, and 40, it will save you plenty of code.

### Hint 3

You can’t have more than three consecutive Roman numerals together.

## Solutions

Solution 1 (Click to Show/Hide)
``````var convertToRoman = function(num) {
var decimalValue = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
var romanNumeral = [
"M",
"CM",
"D",
"CD",
"C",
"XC",
"L",
"XL",
"X",
"IX",
"V",
"IV",
"I"
];

var romanized = "";

for (var index = 0; index < decimalValue.length; index++) {
while (decimalValue[index] <= num) {
romanized += romanNumeral[index];
num -= decimalValue[index];
}
}

return romanized;
};

// test here
convertToRoman(36);
``````

#### Code Explanation

• We start off by creating two arrays with default conversion with matching indices. These are called `decimalValue` and `romanNumeral`. We also create an empty string variable, `romanized`, which will house the final roman number.
• Using a for loop, we loop through the indicies of the `decimalValue` array. We continue to loop until while the value at the current `index` will fit into `num`.
• Next, we add the roman numeral and decrease `num` by the decimal equivalent.
• Finally, we return the value of `romanized`.

Solution 2 (Click to Show/Hide)
``````function convertToRoman(num) {
var romans = ["I", "V", "X", "L", "C", "D", "M"],
ints = [],
romanNumber = [],
numeral = "",
i;
while (num) {
ints.push(num % 10);
num = Math.floor(num / 10);
}
for (i = 0; i < ints.length; i++) {
units(ints[i]);
}
function units() {
numeral = romans[i * 2];
switch (ints[i]) {
case 1:
romanNumber.push(numeral);
break;
case 2:
romanNumber.push(numeral.concat(numeral));
break;
case 3:
romanNumber.push(numeral.concat(numeral).concat(numeral));
break;
case 4:
romanNumber.push(numeral.concat(romans[i * 2 + 1]));
break;
case 5:
romanNumber.push(romans[i * 2 + 1]);
break;
case 6:
romanNumber.push(romans[i * 2 + 1].concat(numeral));
break;
case 7:
romanNumber.push(romans[i * 2 + 1].concat(numeral).concat(numeral));
break;
case 8:
romanNumber.push(
romans[i * 2 + 1]
.concat(numeral)
.concat(numeral)
.concat(numeral)
);
break;
case 9:
romanNumber.push(romans[i * 2].concat(romans[i * 2 + 2]));
}
}
return romanNumber
.reverse()
.join("")
.toString();
}

// test here
convertToRoman(97);
``````

#### Code Explanation

• Create an array of Roman Numerals (`romans`).
• Use a for loop to create an array of the digits (`ints`) in the number.
• Loop through the array of digits (base 10) and as you do, increment the Roman Numeral (base 5) index by 2 (`numeral = romans[i*2]`).
• Within the loop, use Switch Case to push the proper Roman Numerals (backwards) onto that array.
• Reverse the Roman Numerals array and turn it into a string.

Solution 3 (Click to Show/Hide)
``````function convertToRoman(num) {
var romans = [
// 10^i 10^i*5
["I", "V"], // 10^0
["X", "L"], // 10^1
["C", "D"], // 10^2
["M"] // 10^3
],
digits = num
.toString()
.split("")
.reverse()
.map(function(item, index) {
return parseInt(item);
}),
numeral = "";

// Loop through each digit, starting with the ones place
for (var i = 0; i < digits.length; i++) {
// Make a Roman numeral that ignores 5-multiples and shortening rules
numeral = romans[i][0].repeat(digits[i]) + numeral;
// Check for a Roman numeral 5-multiple version
if (romans[i][1]) {
numeral = numeral
// Change occurrences of 5 * 10^i to the corresponding 5-multiple Roman numeral
.replace(romans[i][0].repeat(5), romans[i][1])
// Shorten occurrences of 9 * 10^i
.replace(
romans[i][1] + romans[i][0].repeat(4),
romans[i][0] + romans[i + 1][0]
)
// Shorten occurrences of 4 * 10^i
.replace(romans[i][0].repeat(4), romans[i][0] + romans[i][1]);
}
}

return numeral;
}

// test here
convertToRoman(36);
``````

#### Code Explanation

• Use an array (`romans`) to create a matrix containing the Roman numeral for a given power of 10 and, if available, the Roman numeral for that power of 10 times 5.
• Convert the input number (`num`) to a reversed array of digits (`digits`) so that we can loop through those digits starting with the ones position and going up.
• Loop through each digit, starting with the ones place, and create a Roman numeral string by adding each higher-power Roman numeral to the start of the `numeral` string a number of times equal to `digit`. This initial string ignores the Roman numerals that are a power of 10 times 5 and also ignores shortening rules.
• If the relevant power of 10 has a 5-multiple Roman numeral, in `numeral`, replace 5-in-a-row occurrences with the relevant 5-multiple Roman numeral (i.e., V, L, or D) and shorten occurrences of 9 * 10^i (e.g., VIIII to VIX) and 4 * 10^i (e.g., XXXX to XL). Order is important here!
• Finally, return `numeral`.

Solution 4 (Click to Show/Hide)
``````function convertToRoman(num) {
function getNumeral(digit, lowStr, midStr, nextStr) {
switch (true) {
case digit <= 3:
return lowStr.repeat(digit);
case digit === 4:
return lowStr + midStr;
case digit <= 8: // digits 5-8
return midStr + lowStr.repeat(digit - 5);
default: // digit 9
return lowStr + nextStr
}
}

let str = ""

// Thousands
str += "M".repeat(Math.floor(num/1000));
num %= 1000;

// Hundreds
str += getNumeral(Math.floor(num/100), 'C', 'D', 'M')
num %= 100;

// Tens
str += getNumeral(Math.floor(num/10), 'X', 'L', 'C')
num %= 10;

// Ones
str += getNumeral(num, 'I', 'V', 'X')

return str;
}

convertToRoman(36);
``````

Code explanation:

Roman numerals up to 999 follow a pattern for each digit. For single digit numbers, you have the strings `I`, `V` and `X`. For multiples of ten, you have `X`, `L` and `C`. For multiples of 100, you have `C`, `D` and `M`. So to get the final Roman numeral, just find out the individual string based on those 3 combinations and then concatenate them together.

• Define a function that will take the three strings and a single digit and output its Roman numeral based on the pattern.
• For numbers over 1000, you just repeat `M` for each thousand.
• Use the function to get the Roman Numeral for 100’s, 10’s and 1’s.
23 Likes
``````function convertToRoman(num) {
var romanNumerals = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];//an array of roman numerals in order from largest to smallest
var decimals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];//an array of decimals values that match the index of the roman numerals
var romanized='';//creates an empty string call romanized

for (var i=0;i<decimals.length;i++){ //this loops through the decimals array
while (decimals[i]<=num){ //do something as soon as the indexed number for the decimals array is less than the input number
romanized += romanNumerals[i]; //push the roman numeral with the same index value as the decimals to the romanized string
num -= decimals[i]; //reduce the input number by the matching decimals index number
}
}
return romanized;
}

convertToRoman(36);
``````

I added comments to each part of the code to try to explain what it is doing.

27 Likes

Weird: the basic version seems much more elegant…

29 Likes

Not sure if this is better or worse, but I thought this was a simple, and easier to understand method than what was provided. This may be because it is slightly more hard coded than the other solutions.

function convertToRoman(num) {
num = num.toString();
var str = “” , j = 0, roman = [“I”,“V”,“X”,“L”,“C”,“D”,“M”];
for(i=num.length -1;i>=0;i -= 1){
str = helper(num[i],roman[j],roman[j+1],roman[j+2])+ str;
j += 2;
}
return str;
function helper(num,s,m,b){
var roman = ["",s,s+s,s+s+s,s+m,m,m+s,m+s+s,m+s+s+s,s+b];
return roman[Number(num)];
}
}
convertToRoman(36);

What this is doing is providing the 3 possible characters needed for each digit, for example in the one’s place, the helper functions is called as `helper(num,"I","V","X")`, and the helper functions then takes the provided number and looks it up in the hard coded array to see what combinations of those 3 characters make the number provided. ie 7 = "VII"
We then loop through each digit in the original number starting with the ones digit until we create the entire sting.

1 Like

A tricky puzzle becomes easy when you find the conversion tables

``````function convertToRoman(num) {

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

var roman = '';

while (num > 0) {

// highest mapped decimal less than or equal num
var max = map[0];
map.forEach(function(el) {
if (el.d <= num) {
max = el;
}
});

roman += max.r;
num -= max.d;

}

return roman;
}

convertToRoman(36);``````
16 Likes

This is my solution. Just for the case studying.

``````function convertToRoman(num) {

var rLits = {
1:    'I', // * 5
5:    'V', // * 2
10:   'X', // * 5
50:   'L', // * 2
100:  'C', // * 5
500:  'D', // * 2
1000: 'M'  // * 5
};

num = (num + '').split('');

var str = '';

for (var i = num.length - 1, bit = 1; i >= 0; i--, bit*=10) {
var digit = parseInt(num[i]);
var basePrev = '';
var baseCurr = rLits[5*bit];

if (digit > 5) {
basePrev = rLits[5*bit];
baseCurr = rLits[10*bit];
digit = digit % 5;
} else if (digit === 5) {
str = rLits[5*bit] + str;
}

switch (digit) {
case 1: str = basePrev + rLits[1*bit] + str;
break;
case 2: str = basePrev + rLits[1*bit].repeat(2) + str;
break;
case 3: str = basePrev + rLits[1*bit].repeat(3) + str;
break;
case 4: str = rLits[1*bit] + baseCurr + str;
}
}

return str;
}
``````

Here is the modification of basic solution that uses recursion. Not sure how I can do it better.

``````/* jshint esnext: true */

var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
var romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];
var repeat = (str, count) => new Array(count).fill(str).join('');

function _convertToRoman(num, decimals, romans) {
if (num === 0) {
return '';
}
var div = Math.floor(num / decimals[0]);
var mod = num % decimals[0];
return (repeat(romans[0], div)) + _convertToRoman(mod, decimals.slice(1), romans.slice(1));
}

function convertToRoman(num) {
return _convertToRoman(num, decimalValue, romanNumeral);
}

convertToRoman(36);
``````
1 Like

My pseudo-solution,

``````function convertToRoman(num) {

// 1st: Splice num into unit, deci, cent,
var listinv = num.toString(10).split("").map(Number);
// 2nd: Reverse the numbers,
var list = listinv.reverse();
// 3rd: get the number position
var unit = list[0];
var deci = list [1];
var cent = list [2];
var mill = list [3];

// 4th extraodinary cases
switch (num) {
case 10: num = "X"; return num; break;
case 100: num = "C"; return num; break;
case 500: num = "D"; return num;  break;
case 1000: num = "M"; return num; break;
}
// 5th: case 1: one unit (1-9)

switch (unit) {
case 1: unit = "I";    break;
case 2: unit = "II";   break;
case 3: unit = "III";  break;
case 4: unit = "IV";   break;
case 5: unit = "V";    break;
case 6: unit = "VI";   break;
case 7: unit = "VII";  break;
case 8: unit = "VIII"; break;
case 9: unit = "IX";
}

// if one
if (list.length ===1) {
return unit;
}
// 6th:  Case 2: Two units (10 - 19);
switch (deci) {
case 1: deci = "X"; break;
case 2: deci = "XX"; break;
case 3: deci = "XXX"; break;
case 4: deci = "XL"; break;
case 5: deci = "L"; break;
case 6: deci = "LX"; break;
case 7: deci = "LXX"; break;
case 8: deci = "LXXX"; break;
case 9: deci = "XC";  break;
case 0: deci = ""; break;
}
if (list.length ===2) {
return deci.concat(unit);
}
//7th: case 101 - 999

switch (cent) {
case 1: cent = "C"; break;
case 2: cent = "CC"; break;
case 3: cent = "CCC"; break;
case 4: cent = "CD"; break;
case 5: cent = "D"; break;
case 6: cent = "DC"; break;
case 7: cent = "DCC"; break;
case 8: cent = "DCCC"; break;
case 9: cent = "CM"; break;
case 0: cent = "";
}
if (list.length === 3) {
return cent.concat(deci,unit);
}
// 8th: case 1001- 399

switch (mill) {
case 1: mill = "M"; break;
case 2: mill = "MM"; break;
case 3: mill = "MMM";
}

if (list.length === 4) {
return mill.concat(cent,deci,unit);

}
return list;
}

convertToRoman(1004);

``````
5 Likes

I noticed a pattern in the roman numerals using 3 chars in the same order depending on the length of the number (1000, 100 or 10) , so used that to create a separate fn that uses parameters to set & output the repeating pattern.

``````function numerify (str, n, op) {
let o, m, t;
switch(op) {
case 1000 : o = 'C', m = 'D', t = 'M'; break;
case 100  : o = 'X', m = 'L', t = 'C'; break;
case 10   : o = 'I', m = 'V', t = 'X'; break;
}
if (n >= 4) {
if (n == 4) {
return str+= o + m;
} else if (n == 9) {
return str+= o + t;
} else {
if (n - 5 == 0) {
return str+= m;
} else {
str+= m;
for (let i = 0 ; i < n-5 ; i++) {
str+= o;
}
return str;
}
}
} else {
for (let i = 0 ; i < n ; i++) {
str+= o;
}
return str;
}
}

function convertToRoman(num) {
let str = '';
num = num.toString().split('');

switch (num.length) {
case 4:
for (let i = 0 ; i < num[0] ; i++) {
str+= 'M';
};
num = num.slice(1);
case 3:
str = numerify(str, num[0], 1000);
num = num.slice(1);
case 2:
str = numerify(str, num[0], 100);
num = num.slice(1);
case 1:
str = numerify(str, num[0], 10);
return str;
}
}``````
1 Like

I stringified the argument(num) to array.And unshift the array with “mark” elements(if num is less than 4 digits because the max digits of the course is 3999:P) and reverse it.
With all methods done I can match the index of array with the roman numeric easily using a increase of (index*2).

``````function convert(num) {
var roNum=[];
var newNum=[];
num=num+"";
num=num.split("");

var rule=["I","V","X","L","C","D","M","V_","X_","L_"];
newNum=num.slice();
while(newNum.length<4){
newNum.unshift("mark");
}
newNum.reverse();

for(i=0;i<4;i++){
if(newNum[i]==="mark"){
break;
}
else{
switch(newNum[i]){
case "0":
break;
case "1":roNum.unshift(rule[0+2*i]);
break;
case "2":roNum.unshift(rule[0+2*i]+rule[0+2*i]);
break;
case "3":roNum.unshift(rule[0+2*i]+rule[0+2*i]+rule[0+2*i]);
break;
case "4":roNum.unshift(rule[0+2*i]+rule[1+2*i]);
break;
case "5":roNum.unshift(rule[1+2*i]);
break;
case "6":roNum.unshift(rule[1+2*i]+rule[0+2*i]);
break;
case "7":roNum.unshift(rule[1+2*i]+rule[0+2*i]+rule[0+2*i]);
break;
case "8":roNum.unshift(rule[1+2*i]+rule[0+2*i]+rule[0+2*i]+rule[0+2*i]);
break;
case "9":roNum.unshift(rule[0+2*i]+rule[2+2*i]);
break;
}

}
}
return roNum.join("");
}

convert(3999);``````

Please give me some feedback on my original solution. The Basic Code Solution is very cool, and elegant. I think mine is pretty straightforward so I kind of like it too.

Let me know what you think. Criticize it!

Edit: Thinking about this I realized this is part of the Algorithm Challenge but the only thing that could be considered an algorithm is the switch statement?

``````function convertToRoman(num) {
num = num.toString();

var ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
var tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
var hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"];
var thousands = "M";

// Creates a var for each decimal place
var x = num[num.length - 1];
var xx = num[num.length - 2];
var xxx = num[num.length - 3];
var xxxx = num.slice(0, -3);

// Creates the roman for each decimal place
var num1 = ones[x];
var num2 = tens[xx];
var num3 = hundreds[xxx];
var num4 = thousands.repeat(Number(xxxx));

switch (num.length) {
case 1:
num = num1;
break;
case 2:
num = num2 + num1;
break;
case 3:
num = num3 + num2 + num1;
break;
default:
num = num4 + num3 + num2 + num1;
break;
}
return num;
}

convertToRoman(494);
``````

Solution
Edit: Edited the link, strange it went to a fork of my original link. I think due to changes in the repl.it website.

7 Likes

Basic inelegant solution.

``````function convertToRoman(num) {
var x = num+"";
var y = x.split("");
var j=0;
var z=y.length;
var a=[["I","V","X"],["X","L","C"],["C","D","M"],["M","V","X"]];
var b=[];

for(i=y.length-1;i>=0;i-=1){
y[j]+=("0".repeat(i));
j+=1;
}
for(k=0;k<z;k+=1){
y[k]=parseInt(y[k]);
}
y.reverse();

for(l=0;l<y.length;l+=1){
if(y[l]===0){
b.push("");
} else if(y[l]<parseInt(4+"0".repeat(l))){
b.push(a[l][0].repeat(parseInt(y[l].toString().slice(0,1))));
} else if(y[l]==parseInt(4+"0".repeat(l))){
b.push(a[l][0] + a[l][1]);
} else if(y[l]==parseInt(5+"0".repeat(l))){
b.push(a[l][1]);
} else if(y[l]>parseInt(5+"0".repeat(l)) && y[l]<parseInt(9+"0".repeat(l))){
b.push(a[l][1]+a[l][0].repeat(parseInt(y[l].toString().slice(0,1)-5)));
} else {
b.push(a[l][0]+a[l][2]);
}
}

b.reverse();
b=b.join("");

return b;
}

convertToRoman(501);
``````
1 Like

Did this with quick 'n dirty => iterative function that calls itself again. Not beautiful, not very efficient, but works. I guess one could find more elegant solution based on lists but this was easier…

``````function convertToRoman(num) {
if (num-1000>=0) return "M" + convertToRoman(num-1000);
if (num-900>=0) return "CM" + convertToRoman(num-900);
if (num-500>=0) return "D" + convertToRoman(num-500);
if (num-400>=0) return "CD" + convertToRoman(num-400);
if (num-100>=0) return "C" + convertToRoman(num-100);
if (num-90>=0) return "XC" + convertToRoman(num-90);
if (num-50>=0) return "L" +convertToRoman(num-50);
if (num-40>=0) return "XL" +convertToRoman(num-40);
if (num-10>=0) return "X" + convertToRoman(num-10);
if (num-9>=0) return "IX" + convertToRoman(num-9);
if (num-5>=0) return "V" + convertToRoman(num-5);
if (num-4>=0) return "IV" + convertToRoman(num-4);
if (num-1>=0) return "I" + convertToRoman(num-1);
return "";
}``````
34 Likes

Intermediate Solution

Pros:

1. Short & Simple to understand сompared to other intermediate solutions
2. It replaces addition (from basic solution on topic) by multiplication, in theory it means less operations for process

Minuses:
… I can’t found any minuses

Code Explanation:
In this code I use “integer division” and “division with remainder”. JS has no native support for integer division, so I used this expression: Math.floor(num1 / num2).
For example:
Math.floor(11 / 10) = 1,
Math.floor(11 / 5) = 2,
Math.floor(11 / 100) = 0.

Source code:

``````function convertToRoman(num) {
var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ],
romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ],
romanized = '',
dec, div;

for (var i = 0; i < decimalValue.length; i++) {
dec = decimalValue[i];
div = Math.floor(num / dec);

if(div > 0) {
romanized += romanNumeral[i].repeat(div);
num = num % dec;
}
}

return romanized;
}

convertToRoman(36);``````
3 Likes

Here is my code:

``````function convertToRoman(num) {

var map = { 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 };
var roman ='';
for(var i in map){

while(num >= map[i]){
roman += i;
num -= map[i];
}
}

return roman;
}

//test
convertToRoman(501);``````
19 Likes

After being stuck in an infinite loop trying to get this to work, I finally got it working today!

``````var numbers = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ],
romans = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];

function convertToRoman(input) {
var returnStr = "";

numbers.map(function(number, i) {
while (input >= number) {
returnStr += romans[i];
input -= number;
}
});

return returnStr;
}

convertToRoman(36);``````
7 Likes

I went with a recursive approach. I like this approach because it is relatively simply to implement and understand and scalable (if in theory more Roman Numerals were one day added).

``````function convertToRoman(num) {
var convertTable =
[
[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']
];

convertTable.sort(function(a, b)
{
return b[0] - a[0];
});
return conversion(num, convertTable);
}

function conversion(num, arr)
{
if (num <= 0)
return '';
if (arr[0][0] > num)
{
arr.shift();
return conversion(num, arr);
}

return arr[0][1] + conversion(num - arr[0][0], arr) ;
}``````
2 Likes

Solved this using template based solution.

var numerals = [“I”,“V”,“X”,“L”,“C”,“D”,“M”];
var templates = {
“1”: [0],
“2”: [0,0],
“3”: [0,0,0],
“4”: [0,1],
“5”: [1],
“6”: [1,0],
“7”: [1,0,0],
“8”: [1,0,0,0],
“9”: [0,2]
};

function convertToRoman(num) {
var str = num.toString();
var roman = “”;

for(var i in str){
var decimalPlace = str.length - i - 1;
var template = templates[str[i]];

``````for(var j in template){
roman += numerals[2*decimalPlace + template[j]];
}
``````

}

return roman;
}