Here’s my “Roman Numerals” adventure:
(Sorry, this is gonna be a long post, I’ll explain the journey before I post my solution in the end.)
I knew Roman numerals have signs for 1000, 500, 100 etc. and they usually are added up from left to right, with some exceptions (like 9 - IX instead of VIIII).
So I decided to first get the 1000s, 500s etc. from left to right and deal with the exceptions later. (Bad idea… - Have I mentioned I’m allergic to maths? )
I also started with putting the letters and corresponding numbers into a comment for reference.
Here’s my first step:
function convertToRoman(num) {
let Ms = Math.floor(num/1000);
if(num/1000 > 0) {
num = num%1000;
};
let Ds = Math.floor(num/500);
if(num/500 > 0) {
num = num%500;
};
let Cs = Math.floor(num/100);
if(num/100 > 0) {
num = num%100;
};
let Ls = Math.floor(num/50);
if(num/50 > 0) {
num = num%50;
};
let Xs = Math.floor(num/10);
if (num/10 > 0) {
num = num%10;
}
let Vs = Math.floor(num/5);
if (num/5 > 0) {
num = num%5;
}
let Is = num;
console.log("num: " + num + "Roms: " + Ms + Ds + Cs + Ls + Xs + Vs + Is);
return num;
}
convertToRoman(36);
/*
1000 - M
500 - D
100 - C
50 - L
10 - X
5 - V
1 - I
*/
Next, I decided to shorten the code, because it’s repetitive.
I chose arrays, because they can easily be accessed in loops.
let romanLetters = [["M",0], ["D",0], ["C",0], ["L",0], ["X",0], ["V",0], ["I",0]];
let romanArray = [1000, 500, 100, 50, 10, 5, 1];
function countThem(counter, romanNumber) {
romanLetters[counter][1] = Math.floor(num/romanNumber);
if (num/romanNumber > 0) {
num = num%romanNumber;
};
console.log(romanLetters[counter][1] + " - " + num);
};
for (let i=0; i < romanLetters.length; i++){
countThem(i, romanArray[i]);
};
Next was the hardest part.
I looped through the romanLetters array to add the letters to a string variable.
Of course, now it was time to deal with the exceptions.
The exceptions come up when my code counts 4, eg. 4 Is in 9: VIIII, which needs to be converted to IX.
So I added some if else statements, and it worked for some numbers, but not for all.
I checked some console logs of numbers that worked and didn’t work.
For example:
55
M D C L X V I
0 0 0 1 0 1 0
→ LV
But:
19
M D C L X V I
0 0 0 0 1 1 4
→ XVIIII
I thought I could solve it by checking wether the number is 4, and if it is, add to the string this letter once + the one at [i-2] → IX
Then I needed to prevent the V from being added to the string, so I had the code first check if the next position (as long as a next position exists) is 4, and if so, set current position to 0. And fill in the string after the check. - This worked for some numbers, but not all.
I had to think and came up with:
If before the “4 times” position, there was “0”, add the “4 times” position once + the one before.
If before the “4 times” position, there was “1”, add the “4 times” position once + the one two positions before.
44
M D C L X V I
0 0 0 0 4 0 4
should be XLIV
99
M D C L X V I
0 0 0 1 4 1 4
should be XCIX
I took a break to go for a walk, then put it into code and passed the test shortly before the live class started. Phew! (I didn’t watch live yesterday, because I first wanted to get my own palindrome checker to work, before watching an alternative solution, because I was frustrated after the regex exercises, I had had to copy and paste the solution for some of them because I couldn’t figure them out on my own. - By the way, does anybody know how many people have died from regex?)
Here’s the code that passed the tests before class:
function convertToRoman(num) {
let numCopy = num;
if (num > 3999) {
return console.log("only numbers below 4000, please");
};
console.log("Your number was " + numCopy);
let romanLetters = [["M",0], ["D",0], ["C",0], ["L",0], ["X",0], ["V",0], ["I",0]];
let romanArray = [1000, 500, 100, 50, 10, 5, 1];
function countThem(counter, romanNumber) {
romanLetters[counter][1] = Math.floor(num/romanNumber);
if (num/romanNumber > 0) {
num = num%romanNumber;
};
//console.log(romanLetters[counter][0],romanLetters[counter][1] + " - " + num);
};
// fill romanLetters Array
for (let i=0; i < romanLetters.length; i++){
countThem(i, romanArray[i]);
};
let str="Roman is: ";
//fill string
for (let i = 0; i<romanLetters.length; i++) {
if (romanLetters[i+1]) { //if the next position exists
if ((romanLetters[i+1][1] == 4)) {// and if next position is 4 times
if((romanLetters[i][1])==0) { //and this is 0
// add the next one + this once
str += romanLetters[i+1][0] + romanLetters[i][0];
} else { // and this is not 0
//add the next + the letter before
str += romanLetters[i+1][0] + romanLetters[i-1][0];
}
}
else if ((romanLetters[i][1])==4){ // if this itself is 4 times
// don't do anything, we fixed it before
} else { // if next is not 4 times
for (let j = romanLetters[i][1]; j>0; j--) {
str += romanLetters[i][0]; // add j times this position
};
}
} else { // if this is last position
// and if this position is not 4 times
if ((romanLetters[i][1] != 4)) {
for (let j = romanLetters[i][1]; j>0; j--) {
str += romanLetters[i][0]; // add j times this position
};
}
};
};
console.log(str);
num = str.replace("Roman is: ", "");
//console.log(num);
return num;
}
convertToRoman(36);
/*
1000 - M
500 - D
100 - C
50 - L
10 - X
5 - V
1 - I
*/
As you see, it has a horrible “if-else” monster!! I wasn’t satisfied with this.
I had spent hours on it, and Ramón passed the project without monster code in half an hour!
After class I took another break and thought I could try to make my code simpler, if I added the ‘exception numbers’ to my arrays, similar to the way it was done in class.
Now it looks much better:
function convertToRoman(num) {
let numCopy = num;
if (num > 3999) {
return console.log("only numbers below 4000, please");
};
console.log("Your number was " + numCopy);
let romanLetters = [["M",0], ["CM",0], ["D",0], ["CD",0], ["C",0], ["XC",0], ["L",0], ["XL",0], ["X",0], ["IX",0], ["V",0], ["IV",0], ["I",0]];
let romanArray = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
function countThem(counter, romanNumber) {
romanLetters[counter][1] = Math.floor(num/romanNumber);
if (num/romanNumber > 0) {
num = num%romanNumber;
};
//console.log(romanLetters[counter][0],romanLetters[counter][1] + " - " + num);
};
// fill romanLetters Array
for (let i=0; i < romanLetters.length; i++){
countThem(i, romanArray[i]);
};
//console.log(romanLetters);
let str="Roman is: ";
//fill String
for (let i = 0; i<romanLetters.length; i++) {
for (let j = romanLetters[i][1]; j>0; j--) {
str += romanLetters[i][0];
};
};
console.log(str);
num = str.replace("Roman is: ", "");
//console.log(num);
return num;
}
convertToRoman(97);
/*
1000 - M
500 - D
100 - C
50 - L
10 - X
5 - V
1 - I
*/
I just realized I should check my use of semicolons…
I guess one might improve the code further by merging the arrays into one, like [[1000, “M”,0], [900, “CM”,0] etc. ], and change the rest of the code accordingly.
But I’m not gonna try that. I’m done for today and I think I need to repeat ES6 tomorrow. So I will leave it as it is.
(I’m so tired now, I hope I didn’t write nonsense in this post… ^^’ )