# freeCodeCamp Algorithm Challenge Guide: Roman Numeral Converter

freeCodeCamp Algorithm Challenge Guide: Roman Numeral Converter
0

#1

Remember to use `Read-Search-Ask` if you get stuck. Try to pair program and write your own code

### Problem Explanation:

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

## Hint: 1

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

try to solve the problem now

## 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.

try to solve the problem now

## Hint: 3

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

try to solve the problem now

## Basic Code Solution:

``````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`.

## Intermediate Code Solution:

``````function convertToRoman(num) {
var romans = ["I", "V", "X", "L", "C", "D", "M"],
ints = [],
romanNumber = [],
numeral = "";
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.

## Intermediate Code Solution:

``````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`.

## NOTES FOR CONTRIBUTIONS:

• DO NOT add solutions that are similar to any existing solutions. If you think it is similar but better, then try to merge (or replace) the existing similar solution.
• Categorize the solution in one of the following categories — Basic, Intermediate and Advanced.

See `Wiki Challenge Solution Template` for reference.

Ruman Number Converter
Question about these type of exercices (Roman Numeral Converter & Caesars Cipher)
Would you say this is incredibly inefficient?
Should this be happening? - Algorithm Scripting
As a beginner, how do I know if I'm writing "good" code?
closed #2

opened #3

#4
``````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.

#6

Weird: the basic version seems much more elegant…

#7

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.

#8

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);``````

#9

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;
}
``````

#10

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);
``````

#11

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);

``````

#12

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;
}
}``````

#13

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);``````

#14

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.

#15

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);
``````

#16

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 "";
}``````

#17

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);``````

#18

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

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);``````

#20

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) ;
}``````

#21

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;
}