Roman Numeral Converter - can I use Object instead of arrays?

I have reached an impasse with my code so I took a peek at the Basic Solution to see how far off I was. The most glaring difference was that the Basic Solution uses 2 separate arrays (of decimals and their corresponding roman numerals) and my solution is going to use an object:

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

I am running into many issues which essentially require me to convert to arrays. That is, at some point, I will have to have an array of the properties (of my object, numConvertor) that I can compare against. As per this Q&A, I can do that but then I will end up with an array of strings not numbers. Which seems like I am not only moving towards the Basic Solution approach (i.e. arrays) but moving even further backwards b/c I end up with an array of string which I then have to convert to numbers in order to compare and convert…?

Is there any point in trying to salvage a solution that uses object instead of arrays?

And if so, is what I outlined above the correct course of action?

Thank you!

Your code so far

function convertToRoman(num) {
 //create object of decimal to roman key/values
 var numConvertor = {1000:'M', 900:'CM', 500:'D', 400:'CD', 100:'C', 90:'XC', 50:'L', 40:'XL', 10:'X', 9:'IX', 5:'V', 4:'IV', 1: 'I'};

 //break decimal number into array of units
 function numBreakdown(n) {
   if (n == 0) return [0];
   var arr = [];
   var i = 1;

   while (n > 0) {
     arr.unshift((n % 10) * i);
     n = Math.floor(n / 10);
     i *= 10;
   };
   return arr;
 }

  let arrNumBreakdown = numBreakdown(num);

 //go through array and convert each to a roman numeral based on numConvertor object

 for(var i = 0; i < arrNumBreakdown.length; i++){
   //is there a way to check value of property to compare which is the largest number that is smaller than numBreakdown[i]?
   //Or do I HAVE to use the array methodology as per Basic Solution? 
 }


 //combine results from each iteration into one string value?
}

//convertToRoman(36);

Link to the challenge:
https://learn.freecodecamp.org/javascript-algorithms-and-data-structures/javascript-algorithms-and-data-structures-projects/roman-numeral-converter/

The downside to using an object to maintain that list is, you have no control over the order you’ll get the properties. If, for example, you use let keys = Object.keys(numConvertor) to get the numeric values you use as indexes (indices?) within numConvertor, then simply do something like `numConvertor[keys[i]] to get the appropriate string value, you have no way of knowing if your FIRST value is 1 or 1000.

Arrays are the best way of getting the data in a consistent manner.

1 Like

That said, I took your numConvertor object idea, and tinkered with it a little. If you simply sort the keys in reverse order (greatest to least), then use THAT array (for example, keys[0] = 1000) to get the object value (in the previous, numConvertor[keys[0]] = numConvertor[1000] = "M" ), then you could get that to work nicely.

Of course, that COMPLETELY changes the way you’re parsing through the original num value…

So I went with arrays after all.

And even though I had sneaked a peek at the Basic Solution, I wanted to work through the code without referring to it again. And it ain’t pretty.

First, I gave myself a huge advantage by adding more to the decimals and romans arrays so that more numbers would simply be converted. But I stuck to the basic combinations that were listed here >>> link.

Having done that, I made some Frankenstein-ish code :fearful: But I continue to get stymied by anything higher than 1000.

Any ideas how I can fix this? Obviously, I cannot keep adding to the decimals/romans arrays.

Specifically failing these 2 test-cases right now:

convertToRoman(2014)
convertToRoman(3999)

So the logic should go like this:

  • starting from the largest value, 1000 in this case, is the number divisible by the value one or more time?
  • if so, do two things – append that many of the appropriate letter, and set num to (num - that many units). Aconvenient shortcut here would be the modulo operator, %.
  • keep moving to the next smaller value repeating those steps until num equals zero.

Is that the gist of what you’re doing?

Here’s a link to your object approach in action. Open the console to see it work. https://codepen.io/snowmonkey/pen/qLZwdv?editors=0011

So I decided to start from scratch again. My old code was confusing me more than helping me.

I’ve done several more iterations of code and I keep getting stuck. But let me go back to your rules and ask a question that I tried to answer on my own and is probably what led me down the wrong path.

How can I use % to check how many times a number is divisible by another number? Since % will only return the remainder and not the quotient (which is really what I want isn’t it…?)

So instead of using % (and this might be where I went wrong), I checked for divisibility like this:

let numDivisible = Math.floor(num / decimals[i]);

Is that wrong?

This is my incomplete code so far, in case:

function convertToRoman(num) {
 //create object of decimal to roman key/values
 let decimals = [1000,900,800,700,600,500,400,300,200,100,90,80,70,60,50,40,30,20,10,9,8,7,6,5,4,3,2,1];
 let romans = ['M','CM','DCCC','DCC','DC','D','CD','CCC','CC','C','XC','LXXX','LXX','LX','L','XL','XXX','XX','X','IX','VIII','VII','VI','V','IV','III','II','I',];

 let resultRomans = "";

 for(var i = 0; i < decimals.length; i++){
    //starting from the largest value, 1000 in this case, is the number divisible by the value one or more time?
    let numDivisible = Math.floor(num / decimals[i]);
    console.log(numDivisible);
    if(numDivisible > 1){
      console.log(romans[i]);
      resultRomans += romans[i];
      console.log("This is num - decimals[i]: " + (num - decimals[i]));
      num -= decimals[i]
    }else {
      return romans[decimals.indexOf(num)];
    }
    //if so, do two things – append that many of the appropriate letter, and set num to (num - that many units).
    //Aconvenient shortcut here would be the modulo operator, %.
    //keep moving to the next smaller value repeating those steps until num equals zero.
 }
}

Final code:

[spoiler]```

function convertToRoman(num) {
//create object of decimal to roman key/values

let decimals = [1000,900,800,700,600,500,400,300,200,100,90,80,70,60,50,40,30,20,10,9,8,7,6,5,4,3,2,1];
let romans = [‘M’,‘CM’,‘DCCC’,‘DCC’,‘DC’,‘D’,‘CD’,‘CCC’,‘CC’,‘C’,‘XC’,‘LXXX’,‘LXX’,‘LX’,‘L’,‘XL’,‘XXX’,‘XX’,‘X’,‘IX’,‘VIII’,‘VII’,‘VI’,‘V’,‘IV’,‘III’,‘II’,‘I’,];

let resultRomans = “”;

for(var i = 0; i < decimals.length; i++){
while (decimals[i] <= num) {
resultRomans += romans[i];
num -= decimals[i];
}
}
return resultRomans;
}

Have you tested that with other numbers? Like 2018 or 4321?

You were on the right track with numDivisible. That tells you, for example, that 4321 is divisible by 1000 four times. Then setting num = num %1000 (num “modulo” 1000) makes num = 321. Now, how do you repeat the ‘M’ numDivisible times?

I could have avoided using % by setting

num = num - (numDivisible * decimals[i] );

But the result is the same.