Roman Numeral Converter Challenge

I like the short solutions that hardcode XC, etc.
I opted to stick with single Roman digits only, so my code is a bit more complicated.

function convertToRoman(num) {
  var roman = "";
  var numbers = [
    [1000, "M"],
    [500,  "D"],
    [100,  "C"],
    [50,   "L"],
    [10,   "X"],
    [5,    "V"],
    [1,    "I"]
  ];
  for(var p = 0; p < numbers.length; p++){
    //check for numbers that don't require subtraction
    var c = Math.floor(num / numbers[p][0]);
    num %= numbers[p][0];
    roman += numbers[p][1].repeat(c);
    //that would be enough for long-form roman numbers...
    //check for subtraction - style numbers
    //only if not at 1 already
    if(numbers[p][0] > 1){
      var d = 1;
      //if next highest roman numeral is half of current one, go to the next
      //because numbers like VX are pointless, VX == V
      if (numbers[p][0]/numbers[p+d][0] == 2) {d=2;}
      if (num >= numbers[p][0]-numbers[p+d][0]){
        num -= numbers[p][0]-numbers[p+d][0];
        roman += numbers[p+d][1]+numbers[p][1];
      }
    }
  }
 return roman;
}

Here is my solution:

function convertToRoman(num) {
var romanStr = “”;
var nums = [ 1000, 500, 100, 50, 10, 5, 1 ];
var romans = [ “M”, “D”, “C”, “L”, “X”, “V”, “I”];

		function iterate(divisor,index){
  	  romanStr +=  romans[index].repeat(num/divisor);
  	  num %= divisor;
  	};
  	nums.forEach( iterate );
  
  	return romanStr
  	  .replace(/DCCCC/,"CM").replace(/CCCC/,"CD")
  	  .replace(/LXXXX/,"XC").replace(/XXXX/,"XL")
  	  .replace(/VIIII/,"IX").replace(/IIII/,"IV");
	}
1 Like

I know this is not prety but it works… I have been coding for 80 days and I am still trying to understand how to use filter, map, reduce…

This challenge took me approximately 8 days… my reasoning is basic and honest approach… Now that I solved it I will look at other answers…to improve my code…

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

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

if (num.length == 1) {
return rom1[num];
}
else if (num.length == 2) {
return rom2[num[0]] + rom1[num[1]];
}
else if (num.length == 3) {
return rom3[num[0]] + rom2[num[1]] + rom1[num[2]];
}
else {
return rom4[num[0]] + rom3[num[1]] + rom2[num[2]] + rom1[num[3]];

}

}
convertToRoman(154); `

2 Likes

A bit simplistic, would have liked to have given it more time and put some more smarty logic to work out the numeral.

Without using maths. Not sure if it’s smart but it works ^^

Interesting to see how other people went about it. Here’s my solution, seems lengthier but makes sense to me:


function convertToRoman(num) {
  
if (num > 0 && num < 4000) {
 num = num.toString().split("");

  var romanConv = {
    0: "_",
    1: "I",
    2: "II",
    3: "III",
    4: "IV",
    5: "V",
    6: "VI",
    7: "VII",
    8: "VIII",
    9: "IX",
    10: "X"};
 
 var i = 0;
  while (i<num.length) {
    num.splice(i, 1, romanConv[num[i]]);
    i++;
  }
  
  var length = parseInt(num.length);
  
  if (num.length > 1)  {num[length-2] = num[length-2].replace(/X/g, 'C');
                        num[length-2] = num[length-2].replace(/V/g, 'L');
                        num[length-2] = num[length-2].replace(/I/g, 'X');}
  if (num.length > 2)  {num[length-3] = num[length-3].replace(/X/g, 'M');
                        num[length-3] = num[length-3].replace(/V/g, 'D');
                        num[length-3] = num[length-3].replace(/I/g, 'C');}
  if (num.length > 3)  {num[length-4] = num[length-4].replace(/I/g, 'M');}
  
  num = num.join("");
  num = num.replace(/_/g, "");
 return num;}
 
 else {return "Number must be between 1 - 3999.";}}
  
  
convertToRoman(20);

function convertToRoman(num) {

  function convDigit(digit, highLet, midLet, lowLet) {
    if(digit == 0) return "";
    else if(midLet === undefined && highLet === undefined) return Array(digit+1).join(lowLet);
    else if(digit > 8) return lowLet+highLet;
    else if (digit > 4) return midLet + Array(digit-4).join(lowLet);
    else if (digit == 4) return lowLet+midLet;
    else return Array(digit+1).join(lowLet);    
  }

  var romanNumerals = ["I", "V", "X", "L", "C", "D", "M"]; // able to add more 2 at a time
  //var romanNumerals = ["I", "V", "X", "L", "C", "D", "M", "(barV)", "(barX)", "(barL)", "(barC)", "(barD)", "(barM)"]; 
  var maxPowOf10 = ((romanNumerals.length-1)/2); 
  var digits = [];

  // iterate from lowest to highest digit
  // the highest digit capable of being represented is allowed to exceed 9
  for (var i=0; i<maxPowOf10; i++) {
    digits.push(num%10);
    num = Math.floor(num/10);
  }
  digits.push(num);

  var romanDigits = [];  
  digits.forEach((el, i) => {
    romanDigits.push(convDigit(el, romanNumerals[i*2+2], romanNumerals[i*2+1], romanNumerals[i*2]));
  });

  return romanDigits.reverse().join(""); 

}

convertToRoman(60000);
//convertToRoman(3999999);

I originally started by using a switch case for each digit but couldn’t get it to work, plus it was reaaaally long and repetitive. So I came up with this, and I think it’s pretty neat :slight_smile:

function convertToRoman(num) {
var result = “”;

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’, ‘MM’, ‘MMM’,];

if (num<10){
num = “000”+num;
}
else if (num<100){
num = “00” + num;
}
else if (num<1000){
num = “0” + num;
}
else {num = num.toString();}

result = thousands[num[0]] + hundreds[num[1]] + tens[num[2]] + ones[num[3]];

return result;
}

convertToRoman(1649);

2 Likes

one simple solution

function convertToRoman(num) {
var rec=num.toString();
var niz=rec.split(’’);
var kontraNiz=niz.reverse();
var kraj=[];
switch (kontraNiz[0]){

case "1": kraj.unshift("I");
break;
case "2": kraj.unshift("II");
break;
  case "3": kraj.unshift("III");
break;
  case "4": kraj.unshift("IV");
break;
  case "5": kraj.unshift("V");
break;
  case "6": kraj.unshift("VI");
break;
  case "7": kraj.unshift("VII");
break;
  case "8": kraj.unshift("VIII");
break;
  case "9": kraj.unshift("IX");
break;

}
switch (kontraNiz[1]){

case "1": kraj.unshift("X");
break;
case "2": kraj.unshift("XX");
break;
  case "3": kraj.unshift("XXX");
break;
  case "4": kraj.unshift("XL");
break;
  case "5": kraj.unshift("L");
break;
  case "6": kraj.unshift("LX");
break;
  case "7": kraj.unshift("LXX");
break;
  case "8": kraj.unshift("LXXX");
break;
  case "9": kraj.unshift("XC");
break;

}
switch (kontraNiz[2]){
case “0”:kraj.unshift("");
break;
case “1”: kraj.unshift(“C”);
break;
case “2”: kraj.unshift(“CC”);
break;
case “3”: kraj.unshift(“CCC”);
break;
case “4”: kraj.unshift(“CD”);
break;
case “5”: kraj.unshift(“D”);
break;
case “6”: kraj.unshift(“DC”);
break;
case “7”: kraj.unshift(“DCC”);
break;
case “8”: kraj.unshift(“DCCC”);
break;
case “9”: kraj.unshift(“CM”);
break;
}
switch (kontraNiz[3]){
case “1”: kraj.unshift(“M”);
break;
case “2”: kraj.unshift(“MM”);
break;
case “3”: kraj.unshift(“MMM”);
break;
}
var resenje=kraj.join(’’);
return resenje;
}

convertToRoman(1004);

I have really been enjoying these little exercises. I found FCC last night and I have been burning through as many of these as I can. I was interested to see what others did with this challenge and this is my first visit to the forums.

Anyways, here is how I solved this one. Could probably be cleaner if I didn’t mind decremented romans numerals, but oh well…

    function convertToRoman(num) {
        var retVal = '';
        var numerals = [
            { arabic: 1000, roman: 'M', decr: 100 },
            { arabic: 500,  roman: 'D', decr: 100 },
            { arabic: 100,  roman: 'C', decr: 10 },
            { arabic: 50,   roman: 'L', decr: 10 },
            { arabic: 10,   roman: 'X', decr: 1 },
            { arabic: 5,    roman: 'V', decr: 1 },
            { arabic: 1,    roman: 'I' }
        ];

        numerals.forEach(x => {
            if (num / x.arabic >= 1) {
                retVal += Array(num / x.arabic >> 0).fill(x.roman).join('');
                num = num % x.arabic;   
            }

            if (x.decr !== null) {
                if (num / (x.arabic - x.decr) >= 1) {
                    retVal += numerals.filter(y => y.arabic === x.decr)[0].roman + x.roman;
                    num = num % (x.arabic - x.decr);
                }
            }
        });

        return retVal;
    }
    
    console.log(convertToRoman(36));

I’m actually a little skeptical about solutions to this problem that involve the use of objects. I can’t see all your code, but objects in JavaScript don’t have a guaranteed order to their properties and that can mess with object-based solutions.

If your code works, then it works, but I usually recommend a pair of arrays for this problem, one containing numbers, the other associated Roman numerals in the same index position.

[Mod edit: Section deleted; please do not cast aspersions on the intelligence of other posters.]

Here’s my solution:

function convertToRoman(num) {
 var roma = [
   ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"],
   ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"],
   ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"],
   ["", "M", "MM", "MMM", "MMMM", "MMMMM"],
     ];
 var val=String(num).split("");
 var res="";
 for (var i=val.length-1, j=0; i>=0; --i, ++j)
   res = roma[j][val[i]] + res;
  
  return res;
}

Difficult challenge for me, and had to look around for some help. I came up with this:

function convertToRoman(num) {  
  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];
  
  var result = [];
  
  while (num > 0) {
    arabic.forEach(function(key, i) {
      var times = Math.floor(num / key);
      var remainder = num % key;
      
      result.push(roman[i].repeat(times));
      num = remainder;
    });
  }
  
  return result.join("");
}

Hey all,
Came up with this. I previously put it in an if/else/else/else statement, but this is already a lot better. I saw the solution up with the hardcoding of the edge cases (4 & 9) and it is way more elegant, but it follows the same idea.

function convertToRoman(num) {
  var digits= num.toString(10).split("").map(function(t){return parseInt(t)});
  var romanNumerals = ["I", "V", "X", "L", "C", "D", "M"];
  var totalDigits = digits.length;
  var answer = "";
  
  for (currentDigit = 0; currentDigit<totalDigits; currentDigit++){
    powerOfTen = totalDigits-currentDigit-1; // calculate the power of ten of the current digit x = 0, x0=1, x00=2, x000=3;
    switch (digits[currentDigit]) {
      case 0: break;  // in case of 0, don't add anything to the result
      case 1:
      case 2:
      case 3: // in case of 1,2,3, add x times the right roman numeral.
        answer += romanNumerals[powerOfTen*2].repeat(digits[currentDigit]);
        break;
      case 4: // in case of 4, use one of the numerals and one of the 5x numeral, 1 up in the array.
        answer += (romanNumerals[powerOfTen*2] + romanNumerals[powerOfTen*2 + 1]);
        break;
      case 5: // in case of 5, add the 5x numeral
        answer += romanNumerals[powerOfTen*2+1];
        break;
      case 6:
      case 7: 
      case 8: // 6,7,8, add 5x numeral followed by x-5 times single numeral
        answer += romanNumerals[powerOfTen*2+1] + romanNumerals[powerOfTen*2].repeat(digits[currentDigit]-5);
        break;
      case 9: // 9, add single numeral followed by 10x numeral.
      answer += (romanNumerals[powerOfTen*2] + romanNumerals[powerOfTen*2 + 2]);
    }
  }
   
  return answer;
}

I did something similar, but I used an object instead of dual-arrays:

1 Like

:disappointed:

function convertToRoman(num) {
  var numArray = num.toString().split(''),
      argLen = num.toString().length,
      onesPlace,
      tensPlace,
      hunsPlace,
      thouPlace,
      answer = "",
      romanNumerals = {
           0: "",
           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"
      };
  
  switch (argLen) {
    case 1:
      onesPlace = romanNumerals[numArray[0]];
      answer = onesPlace;
      break;
    case 2:
      onesPlace = romanNumerals[numArray[1]];
      var x = numArray[0] * 10;
      tensPlace = romanNumerals[x];
      answer = tensPlace.concat(onesPlace);
     break;
    case 3: 
      onesPlace = romanNumerals[numArray[2]]; 
      x = numArray[1] * 10;
      tensPlace = romanNumerals[x];
      var y = numArray[0] * 100;
      hunsPlace = romanNumerals[y];
      answer = hunsPlace.concat(tensPlace, onesPlace);
     break;
    case 4:
      onesPlace = romanNumerals[numArray[3]]; 
      x = numArray[2] * 10;
      tensPlace = romanNumerals[x];
      y = numArray[1] * 100;
      hunsPlace = romanNumerals[y];
      var z = numArray[0] * 1000;
      thouPlace = romanNumerals[z];
      answer = thouPlace.concat(hunsPlace, tensPlace, onesPlace);
     break;
  }   
  
  
 return answer;
}

convertToRoman(3999);

That is some elegant code right there!

Hi,
This is my solution :

function convertToRoman(num) {
 
  var number = num.toString().split('');
 
  var numberUnits  = number.splice(-1);
  var numberTens  = number.splice(-1);
  var numberHundreds  = number.splice(-1);
  var numberThousands = number.splice(-1);
  var result = [];
  
  var units = {1:"I", 2:"II", 3:"III", 4:"IV", 5:"V", 6:"VI", 7:"VII", 8:"VIII", 9:"IX"};
  var tens = {1:"X", 2:"XX", 3:"XXX", 4:"XL", 5:"L", 6:"LX", 7:"LXX", 8:"LXXX", 9:"XC"};
  var hundreds = {1:"C", 2:"CC", 3:"CCC", 4:"CD", 5:"D", 6:"DC", 7:"DCC", 8:"DCCC", 9:"CM"};
  var thousands = {1:"M", 2:"MM", 3:"MMM"}; 
   
  result.push(thousands[numberThousands], hundreds[numberHundreds], tens[numberTens],  units[numberUnits]);

  return result.join('');
}

Cheers and happy coding :slight_smile:

I found a step by step, psuedo-code algorithim that explained the problem perfectly, I just had to re-create it. I was very close to solving it myself, but it had so many nested loops that I just had to start again, rather than try to refactor them.

2 Likes

Very cool!

Any chance you remember where you found it?

I’m in desperate need of some more guidance for these challenges. Easy enough to find answers or complicated discussions, but tough to find the sweet spot of helpful guidance.
If not, no worries! :sunglasses: