Roman Numeral Converter Challenge

This was my solution to the challenge, but I also tried to make it into an actual web-page. Then I realized that it’s only converting correct up to 3999. So I ended up doing a bit more coding to get a real converter. Link to Github at the bottom. Still not sure if it’s correct though…

function convertToRoman(num) {
numString = num.toString();
numArray = numString.split("");

var positionOne = ["", “I”, “II”, “III”, “IV”, “V”, “VI”, “VII”, “VIII”, “IX”];
var positionTen = ["", “X”, “XX”, “XXX”, “XL”, “L”, “LX”, “LXX”, “LXXX”, “XC”];
var positionHundred = ["", “C”, “CC”, “CCC”, “CD”, “D”, “DC”, “DCC”, “DCCC”, “CM”];
var positionThousand = ["", “M”, “MM”, “MMM”, “MV”, “V”, “VI”, “VII”, “VIII”, “IX”];

if (numArray.length === 1) {
  return positionOne[numArray];
} else if (numArray.length === 2) {
  return positionTen[numArray[0]] + positionOne[numArray[1]];
} else if (numArray.length === 3) {
  return positionHundred[numArray[0]] + positionTen[numArray[1]] + positionOne[numArray[2]];
} else if (numArray.length === 4) {
  return positionThousand[numArray[0]] + positionHundred[numArray[1]] + positionTen[numArray[2]] + positionOne[numArray[3]];

}
}

https://pialise.github.io/romanNumeralConverter/

Hi, I don’ think that my solution is the better solution, but I don’t see any solution like mine:

function convertToRoman(num) {
var arrayRoman = {1:‘I’,2:‘II’,3:‘III’,4:‘IV’,5:‘V’,6:‘VI’,7:‘VII’,8:‘VIII’,9:‘IX’,10:‘X’,20:‘XX’,30:‘XXX’,40:‘XL’,50:‘L’,60:‘LX’,70:‘LXX’,80:‘LXXX’,90:‘XC’,100:‘C’,200:‘CC’,300:‘CCC’,400:‘CD’,500:‘D’,600:‘DC’,700:‘DCC’,800:‘DCCC’,900:‘CM’,1000:‘M’, 2000: ‘MM’, 3000: ‘MMM’};

var arrayAux = [1000, 100, 10, 1];
var arrayReturn = [];

for(var i = 0; i < arrayAux.length ; i++){

arrayReturn.push(arrayRoman[Math.floor(num / arrayAux[i]) * arrayAux[i] ]);

num = num%arrayAux[i];

}

return arrayReturn.join(’’);
}

my solution

function convertToRoman(num) {
  let rom = ["I", "V", "X", "L", "C", "D", "M"],
      snum = num.toString().split('').reverse().join(''),
      res = [];
  for (let i = 0, len = snum.length, n, r; i < len; i++){
    r = i*2;
    n = parseInt(snum[i]);
    switch(n){
      case 1:case 2:case 3:
        res.unshift(rom[r].repeat(n));
        break;
      case 4:
        res.unshift(rom[r]+rom[r+1]);
        break;
      case 5:
        res.unshift(rom[r+1]);
        break;
      case 6:case 7:case 8:
        res.unshift(rom[r+1]+(rom[r].repeat(n-5)));
        break;
      case 9:
        res.unshift(rom[r]+rom[r+2]);
        break;
      case 10:
        res.unshift(rom[r+2]);
        break;
    }
  }
  return res.join('');
}

my not so elegant solution

 function convertToRoman(num) {

        var breakDown = [];
        var tempStr = [];
        var one = 'I',
            five = 'V',
            ten = 'X',
            fifty = 'L',
            hundred = 'C',
            fiveHundred = 'D',
            thousand = 'M';

        if(num <= 10) {
            breakDown.push(num);
        } else if( num >= 1000) {
            for (var x = 1000, temp1 = 0; x >= 1; x /= 10) {
                if (num / x >= 1) {
                    temp1 = num % x;
                    breakDown.push(num - temp1);
                    num = temp1;

                } else if(num < 10 && x === 1) {
                    breakDown.push(num);
                } else {
                    breakDown.push(0);
                }
            }
        } else if(num > 100 && num < 1000) {
            for (var y = 100, temp2 = 0; y >= 1; y /= 10) {
                if (num / y >= 1) {
                    temp2 = num % y;
                    breakDown.push(num - temp2);
                    num = temp2;

                } else if(num < 10 && y === 1) {
                    breakDown.push(num);
                } else {
                    breakDown.push(0);
                }
            }
        } else {
            for (var z = 10, temp3 = 0; z >= 1; z /= 10) {
                if (num / z >= 1) {
                    temp3 = num % z;
                    breakDown.push(num - temp3);
                    num = temp3;

                } else if(num < 10 && z === 1) {
                    breakDown.push(num);
                } else {
                    breakDown.push(0);
                }
            }
        }

        for(var i = 0; i < breakDown.length; i++) {

            if(breakDown[i] >= 1000) {
                /* for(var j = 0; j < (breakDown[j] / 1000); j++) {
                    tempStr.push(thousand);
                } */
                switch(breakDown[i]) {
                    case 1000:
                        tempStr.push(thousand);
                        break;
                    case 2000:
                        tempStr.push(thousand + thousand);
                        break;
                    case 3000:
                        tempStr.push(thousand + thousand + thousand);
                        break;
                    case 4000:
                        tempStr.push(thousand + thousand + thousand + thousand);
                        break;
                }
            } else if(breakDown[i] === 1) {
                tempStr.push(one);
            } else if(breakDown[i] === 2) {
                tempStr.push(one + one);
            } else if(breakDown[i] === 3) {
                tempStr.push(one + one + one);
            } else if(breakDown[i] === 4) {
                tempStr.push(one + five);
            } else if(breakDown[i] === 5) {
                tempStr.push(five);
            } else if(breakDown[i] === 6) {
                tempStr.push(five + one);
            } else if(breakDown[i] === 7) {
                tempStr.push(five + one + one);
            } else if(breakDown[i] === 8) {
                tempStr.push(five + one + one + one);
            } else if(breakDown[i] === 9) {
                tempStr.push(one + ten);
            } else if(breakDown[i] === 10) {
                tempStr.push(ten);
            } else if(breakDown[i] === 20) {
                tempStr.push(ten + ten);
            } else if(breakDown[i] === 30) {
                tempStr.push(ten + ten + ten);
            } else if(breakDown[i] === 40) {
                tempStr.push(ten + fifty);
            } else if(breakDown[i] === 50) {
                tempStr.push(fifty);
            } else if(breakDown[i] === 60) {
                tempStr.push(fifty + ten);
            } else if(breakDown[i] === 70) {
                tempStr.push(fifty + ten + ten);
            } else if(breakDown[i] === 80) {
                tempStr.push(fifty + ten + ten + ten);
            } else if(breakDown[i] === 90) {
                tempStr.push(ten + hundred);
            } else if(breakDown[i] === 100) {
                tempStr.push(hundred);
            } else if(breakDown[i] === 200) {
                tempStr.push(hundred + hundred);
            } else if(breakDown[i] === 300) {
                tempStr.push(hundred + hundred + hundred);
            } else if(breakDown[i] === 400) {
                tempStr.push(hundred + fiveHundred);
            } else if(breakDown[i] === 500) {
                tempStr.push(fiveHundred);
            } else if(breakDown[i] === 600) {
                tempStr.push(fiveHundred + hundred);
            } else if(breakDown[i] === 700) {
                tempStr.push(fiveHundred + hundred + hundred);
            } else if(breakDown[i] === 800) {
                tempStr.push(fiveHundred + hundred + hundred + hundred);
            } else if(breakDown[i] === 900) {
                tempStr.push(hundred + thousand);
            } else {
                tempStr.push('');
            }

        }
        num = tempStr.join('');

        return num;
    }

    convertToRoman(36);

Not so compact but it works and it’s all original. Basically a bunch of if statements that reduces the num down as it progresses and at the same time builds up the roman numeral.

function convertToRoman(num) {
  
  var romanNum = "";
  
  if (num >= 1000){
    var M = Math.floor(num/1000);
    num = num - (M * 1000);
    for (var z = 0; z < M; z++){
      romanNum = romanNum.concat("M");
    }
  }
  
  if (num < 1000 & num >= 900){
    var CM = Math.floor(num/900);
    num = num - (CM * 900);
    romanNum = romanNum.concat("CM");
  }
  
  if (num >= 500){
    num = num - 500;
    romanNum = romanNum.concat("D");
  }
    
  if (num >= 100){
    var C = Math.floor(num/100);
    num = num - (C * 100);
    for (var z = 0; z < C; z++){
      romanNum = romanNum.concat("C");
    }
  }
    
  if (num < 100 & num >= 90){
    var XC = Math.floor(num/90);
    num = num - (XC * 90);
    romanNum = romanNum.concat("XC");
  }
  
  if (num >= 50){
    num = num - 50;
    romanNum = romanNum.concat("L");
  }
  
  if (num >= 40){
    var XL = Math.floor(num/40);
    num = num - (XL * 40);
    romanNum = romanNum.concat("XL");
  }
  
  if (num >= 10){
    var X = Math.floor(num/10);
    num = num - (X * 10);
    for (var z = 0; z < X; z++){
      romanNum = romanNum.concat("X");
    }
  }
    
  if (num < 10 & num >= 9){
    var IX = Math.floor(num/9);
    num = num - (IX * 9);
    romanNum = romanNum.concat("IX");
  }
    
    
  if (num >= 5){
    num = num - 5;
    romanNum = romanNum.concat("V");
  }
    
    
  if (num == 4){
    var IV = Math.floor(num/4);
    num = num - (IV * 4);
    romanNum = romanNum.concat("IV");
  }
    
    
  if (num >= 1){
    var I = Math.floor(num/1);
    num = num - (I * 1);
    for (var z = 0; z < I; z++){
      romanNum = romanNum.concat("I");
    }
  }
    
  
 return romanNum;
}

Really nice job. This is a clever approach. It’s listed here as the basic solution but ought to be swapped with the “intermediate” solution.

PS: here’s me getting an emoji badge: :wink:

this was my solution. Not proud.

  • Convert num to a string with leading 0s
  • Slice to 4 places and split num into an array.
  • Map array to return value, substituting corresponding roman numerals:
  • e.g., 2 in ones place is romanArr[0][2]; in tens place maps to romanArr[1][2]
  • Math.abs(index-(myNumber.length-1)) reverses array order… i.e., 0 index looks at 4th place
  • Join and return
function convertToRoman(num) {
	var myNumber = ("000"+num).slice(-4).split("");
	var romanArr=[];
	romanArr[0] = ["","I","II","III","IV","V","VI","VII","VIII","IX"];
	romanArr[1] = ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"];
	romanArr[2] = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"];
	romanArr[3] = ["","M","MM","MMM","MV","V","VM","XMM","XMMM","MW"];
	return myNumber.map( function(value,index) {
		return romanArr[Math.abs(index-(myNumber.length-1))][value];
	}).join("");
}
convertToRoman(3);
1 Like

function convertToRoman(num) {
var newArray = [];

while (num>999){
newArray.push(“M”);
num = num-1000;
}
while (num>499){
newArray.push(“D”);
num = num-500;
}
while (num>99){
newArray.push(“C”);
num = num-100;
}
while (num>49){
newArray.push(“L”);
num = num-50;
}
while (num>9){
newArray.push(“X”);
num = num-10;
}
while (num>4){
newArray.push(“V”);
num = num-5;
}
while (num>0){
newArray.push(“I”);
num–;
}
return newArray.join("").replace(/DCCCC/g, ‘CM’).replace(/CCCC/g, ‘CD’).replace(/LXXXX/g, ‘XC’).replace(/XXXX/g, ‘XL’).replace(/VIIII/g, ‘IX’).replace(/IIII/g, ‘IV’);
}

Yeah, I had a hard coded version that I didn’t like so I tried something more algoruthmic:

$(function(){ 

  function convertToRoman(num) {
    var romanNumeralChars = ["I", "V", "X", "L", "C", "D", "M"];  // array of RN symbols
    var romanNumeral = []; // array to which we will push each symbol
     
    function addChars(power) {
      var factor = Math.pow(10, power); // which "place" are we disecting
      var base = power*2; // the index into romanNumeralChars for the "place"
      
      if (num>=factor) { // is there any portion in this "place"
        var top = Math.floor(num/factor); // lop off the portion of this "place" and reduce to a single digit
        num -= top*factor; // remove that lopped off potion so we can continue later with the remainder

        switch(top) {
          case 1:
          case 2:
          case 3:
            for (var j = 0 ; j < top ; j++) {
              romanNumeral.push(romanNumeralChars[base]);
            }
            break;    
          case 4:
            romanNumeral.push(romanNumeralChars[base]);
            romanNumeral.push(romanNumeralChars[base+1]);
            break;               
          case 5:
          case 6:        
          case 7:       
          case 8:
            romanNumeral.push(romanNumeralChars[base+1]);          
            for (var k = 0 ; k < (top - 5) ; k++) {
              romanNumeral.push(romanNumeralChars[base]);
            }
            break;               
          case 9:
            romanNumeral.push(romanNumeralChars[base]);
            romanNumeral.push(romanNumeralChars[base+2]);
            break;          
        } // switch
      } // if 
    } // addChars
    
    if (num < 0 || num > 3999 || !Number.isInteger(num)) { // making sure it's and integet between 0 and 3999
      return num + " is invalid input";
    }
    
    for (var i = 3 ; i >= 0 ; i--) { // index through the powers
      addChars(i);
    }
    return romanNumeral.join("");
  } // convertToRoman()
  
  var testArr = [1, 2, 3, 4, 5, 9, 12, 16, 29, 44, 45, 68, 83, 97, 99, 500, 501, 649, 
                 798, 891, 1000, 1004, 1006, 1023, 2014, 3099, 3479, 3694, 3999, -6, "apple"];

  for (var i = 0 ; i < testArr.length ; i++) {
    console.log("Converting " + testArr[i] + " ---> " + convertToRoman(testArr[i]));
  }
});

It definitely had fewer lines of code than my original but not as small of some of the others I see here - though they hard coded a little and I was interested in what the algorithm would be.

My solution is perhaps lower than intermediate scripting level, but works:

function convertToRoman(num) {

/convert number to a string and split it to an array;
we reverse the array to make possible get subNums by index, coz as input
we have units in any way, and its index (the last without reverse) will be
changed each time by other (previous) subNums;
/
var numArr = num.toString().split("").reverse();

// after reverse the array, get subNums by index;
var units = numArr[0];
var tens = numArr[1];
var hundreds = numArr[2];

// but thousands are all the subnums after hundreds
var thousands = numArr.slice(3).reverse().join("");

// arrays with roman numbers
var romUnitsArr = [‘I’, ‘II’, ‘III’, ‘IV’, ‘V’, ‘VI’, ‘VII’, ‘VIII’, ‘IX’];
var romTensArr = [‘X’, ‘XX’, ‘XXX’, ‘XL’, ‘L’, ‘LX’, ‘LXX’, ‘LXXX’, ‘XC’];
var romHundredsArr = [‘C’, ‘CC’, ‘CCC’, ‘CD’, ‘D’, ‘DC’, ‘DCC’, ‘DCCC’, ‘CM’];
var romans = [];

// get romans
var romUnits = romUnitsArr[units - 1];
var romTens = romTensArr[tens - 1];
var romHundreds = romHundredsArr[hundreds - 1];
var romThousands = "’’;
for (var i=0; i<thousands; i++) {
romThousands += ‘M’;
}

//push all romans into array in correct order
romans.push(romThousands);
romans.push(romHundreds);
romans.push(romTens);
romans.push(romUnits);

// filter out all booleans (delete “”, null)
var romanNumber = romans.filter(Boolean).join("");
return romanNumber;
}
convertToRoman(891);

Can you please help me understand why did you initialize values for accumulator and did not directly use s and calculated variables?

I tried to directly use the variable but I got incorrect answer.

I came up with almost the same!
But is there a significant difference? performance? security?

function convertToRoman(num) {
  var roman = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX","V", "IV", "I"],
      arabic = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
      result = "";
  
  while (num > 0){
    for (var i = 0; i < arabic.length; i++){
      if (num - arabic[i] >= 0){
        result += roman[i];
        num -= arabic[i];
        break ;
      }
    }
  }
  return result;
}
1 Like

I used if else because I couldn’t think of anything else. Is this too bad??

  
     function convertToRoman(num) {
      var original = num;
      var arr = num.toString().split("").map(function(t){return parseInt(t);});
      var ones = ["","I","II","III","IV","V","VI","VII","VIII","IX"];
      var tens = ["","X","XX","XXX","XL","L","LX","LXX","LXXX","XC"];
      var hun  = ["", "C","CC","CCC","CD","D","DC","DCC","DCCC","CM"];
      var thou = ["","M"];

      if (arr.length === 1) {
        arr[0] = ones[arr[0]];
      } else if (arr.length === 2){
        arr[0] = tens[arr[0]]; 
        arr[1] = ones[arr[1]];
      } else if (arr.length === 3) {
        arr[0] = hun[arr[0]]; 
        arr[1] = tens[arr[1]];
        arr[2] = ones[arr[2]];
      } else if (arr.length > 3) {
        arr[0] = thou[1].repeat(arr[0]);
        arr[1] = hun[arr[1]]; 
       arr[2] = tens[arr[2]];
        arr[3] = ones[arr[3]];
      }
      return arr.join("");
    }

    convertToRoman(8355);

You should be proud!! It’s shorter and easy to read than most I’ve seen :slight_smile:

1 Like

Yes, it was a tough one. Looks good.

I might have used a switch statement instead of the if-else chain, but that’s more style than function.

Oh cool, I’ll make a note to try switch later. Thanks!

@rmdawson71 I think it’s genius! I’ll probably delete my solution and try to replicate yours, since it’s much compact and elegant. Great job :slight_smile:

When I started working on this I had a series of if statements… it was ridiculous. Here’s what I finally came up with. It’s amazing to see the number of ways people have solved this challenge!

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

function convertToRoman(num) {
  var result = [];
    for (i = 0; i < roman.length; i++) {
      while ((num - arabic[i]) >= 0) {
          result.push(roman[i]);
          num -= arabic[i];
      } 
  }
  return result.join("");
}
1 Like

I think @paintingfire’s solution is great. Very clear, fast, and concise.

In this situation, where there are clear limits to the range (0 to 3999) and ways in which the values can be expressed, it makes more sense to use smart arrays than complicated formulas. Nevertheless, I took the formula approach.

I read on StackOverflow that if-then statements are faster than case-switch statements when dealing with values that are not exact (i.e. > 4, < 4), so I opted for the if-then approach.

I’m also impressed with all the different solutions others came up with!

function convertToRoman(num) {
  
  if (!(num > 0 && num < 4000)) return false; // just to be safe
  
  var numStr = num.toString();
  var x = (numStr.length - 1) * 2; // roman numeral array index
  var rnArr = ["I", "V", "X", "L", "C", "D", "M"];
  var rnStr = "";
  
  for (var i = 0; i < numStr.length; i++, x-=2) {
    
    var digit = parseInt(numStr.charAt(i), 10);
    
    if (digit == 9) {
      rnStr += rnArr[x] + rnArr[x + 2];
    } else if (digit > 4) {
      rnStr += rnArr[x + 1];
      rnStr += Array(digit - 4).join(rnArr[x]);
    } else if (digit == 4) {
      rnStr += rnArr[x] + rnArr[x + 1];
    } else {
      rnStr += Array(digit + 1).join(rnArr[x]);
    }
  }
  
  return rnStr;
}
1 Like

way cleaner than my solution was :))