Algorithm to convert Roman Numerals with Javascript [duplicate] - javascript

This question already has answers here:
Convert a number into a Roman numeral in JavaScript
(93 answers)
Closed 6 years ago.
I want to implement Algorithm which converts Roman numeral to Arabic with Javascript. Using the following suggested methods.
Array.prototype.splice()
Array.prototype.indexOf()
Array.prototype.join()
I have already found the algorithm which solves this task
function convertToRoman(num) {
var numeric = [ 5000,4000,1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
var roman = [ 'V\u0305','I\u0305V\u0305','M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];
var output = '', i, len = numeric.length;
for (i = 0; i < len; i++) {
while (numeric[i] <= num) {
output += roman[i];
num -= numeric[i];
}
}
return output;
}
convertToRoman(4999);
Nevertheless i'm curious how to implement an algorithm with above mentioned methods.
Thanks, please do not judge me harshly, i'm a beginner programmer.

I think the question has already been answered:
https://stackoverflow.com/a/9083857/4269495
Number to Roman Numeral:
Number.prototype.toRoman= function () {
var num = Math.floor(this),
val, s= '', i= 0,
v = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
r = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
function toBigRoman(n) {
var ret = '', n1 = '', rem = n;
while (rem > 1000) {
var prefix = '', suffix = '', n = rem, s = '' + rem, magnitude = 1;
while (n > 1000) {
n /= 1000;
magnitude *= 1000;
prefix += '(';
suffix += ')';
}
n1 = Math.floor(n);
rem = s - (n1 * magnitude);
ret += prefix + n1.toRoman() + suffix;
}
return ret + rem.toRoman();
}
if (this - num || num < 1) num = 0;
if (num > 3999) return toBigRoman(num);
while (num) {
val = v[i];
while (num >= val) {
num -= val;
s += r[i];
}
++i;
}
return s;
};

Related

I am trying to use a if statement to determine if the roman numerals is 4, 9, 40, 90, 400, 900 but I am getting undefined [duplicate]

How can I convert integers into roman numerals?
function romanNumeralGenerator (int) {
}
For example, see the following sample inputs and outputs:
1 = "I"
5 = "V"
10 = "X"
20 = "XX"
3999 = "MMMCMXCIX"
Caveat: Only support numbers between 1 and 3999
There is a nice one here on this blog I found using google:
http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
function romanize (num) {
if (isNaN(num))
return NaN;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}
function romanize(num) {
var lookup = {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},roman = '',i;
for ( i in lookup ) {
while ( num >= lookup[i] ) {
roman += i;
num -= lookup[i];
}
}
return roman;
}
Reposted from a 2008 comment located at: http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
VIEW DEMO
I don't understand why everyone's solution is so long and uses multiple for loops.
function convertToRoman(num) {
var roman = {
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 str = '';
for (var i of Object.keys(roman)) {
var q = Math.floor(num / roman[i]);
num -= q * roman[i];
str += i.repeat(q);
}
return str;
}
I've developed the recursive solution below. The function returns one letter and then calls itself to return the next letter. It does it until the number passed to the function is 0 which means that all letters have been found and we can exit the recursion.
var romanMatrix = [
[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']
];
function convertToRoman(num) {
if (num === 0) {
return '';
}
for (var i = 0; i < romanMatrix.length; i++) {
if (num >= romanMatrix[i][0]) {
return romanMatrix[i][1] + convertToRoman(num - romanMatrix[i][0]);
}
}
}
These functions convert any positive whole number to its equivalent Roman Numeral string; and any Roman Numeral to its number.
Number to Roman Numeral:
Number.prototype.toRoman= function () {
var num = Math.floor(this),
val, s= '', i= 0,
v = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
r = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
function toBigRoman(n) {
var ret = '', n1 = '', rem = n;
while (rem > 1000) {
var prefix = '', suffix = '', n = rem, s = '' + rem, magnitude = 1;
while (n > 1000) {
n /= 1000;
magnitude *= 1000;
prefix += '(';
suffix += ')';
}
n1 = Math.floor(n);
rem = s - (n1 * magnitude);
ret += prefix + n1.toRoman() + suffix;
}
return ret + rem.toRoman();
}
if (this - num || num < 1) num = 0;
if (num > 3999) return toBigRoman(num);
while (num) {
val = v[i];
while (num >= val) {
num -= val;
s += r[i];
}
++i;
}
return s;
};
Roman Numeral string to Number:
Number.fromRoman = function (roman, accept) {
var s = roman.toUpperCase().replace(/ +/g, ''),
L = s.length, sum = 0, i = 0, next, val,
R = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
function fromBigRoman(rn) {
var n = 0, x, n1, S, rx =/(\(*)([MDCLXVI]+)/g;
while ((S = rx.exec(rn)) != null) {
x = S[1].length;
n1 = Number.fromRoman(S[2])
if (isNaN(n1)) return NaN;
if (x) n1 *= Math.pow(1000, x);
n += n1;
}
return n;
}
if (/^[MDCLXVI)(]+$/.test(s)) {
if (s.indexOf('(') == 0) return fromBigRoman(s);
while (i < L) {
val = R[s.charAt(i++)];
next = R[s.charAt(i)] || 0;
if (next - val > 0) val *= -1;
sum += val;
}
if (accept || sum.toRoman() === s) return sum;
}
return NaN;
};
I personally think the neatest way (not by any means the fastest) is with recursion.
function convert(num) {
if(num < 1){ return "";}
if(num >= 40){ return "XL" + convert(num - 40);}
if(num >= 10){ return "X" + convert(num - 10);}
if(num >= 9){ return "IX" + convert(num - 9);}
if(num >= 5){ return "V" + convert(num - 5);}
if(num >= 4){ return "IV" + convert(num - 4);}
if(num >= 1){ return "I" + convert(num - 1);}
}
console.log(convert(39));
//Output: XXXIX
This will only support numbers 1-40, but it can easily be extended by following the pattern.
This version does not require any hard coded logic for edge cases such as 4(IV),9(IX),40(XL),900(CM), etc. as the others do.
I have tested this code against a data set from 1-3999 and it works.
TLDR;
This also means this solution can handle numbers greater than the maximum roman scale could (3999).
It appears there is an alternating rule for deciding the next major roman numeral character. Starting with I multiply by 5 to get the next numeral V and then by 2 to get X, then by 5 to get L, and then by 2 to get C, etc to get the next major numeral character in the scale. In this case lets assume "T" gets added to the scale to allow for larger numbers than 3999 which the original roman scale allows. In order to maintain the same algorithm "T" would represent 5000.
I = 1
V = I * 5
X = V * 2
L = X * 5
C = L * 2
D = C * 5
M = D * 2
T = M * 5
This could then allow us to represent numbers from 4000 to 5000; MT = 4000 for example.
Code:
function convertToRoman(num) {
//create key:value pairs
var romanLookup = {M:1000, D:500, C:100, L:50, X:10, V:5, I:1};
var roman = [];
var romanKeys = Object.keys(romanLookup);
var curValue;
var index;
var count = 1;
for(var numeral in romanLookup){
curValue = romanLookup[numeral];
index = romanKeys.indexOf(numeral);
while(num >= curValue){
if(count < 4){
//push up to 3 of the same numeral
roman.push(numeral);
} else {
//else we had to push four, so we need to convert the numerals
//to the next highest denomination "coloring-up in poker speak"
//Note: We need to check previous index because it might be part of the current number.
//Example:(9) would attempt (VIIII) so we would need to remove the V as well as the I's
//otherwise removing just the last three III would be incorrect, because the swap
//would give us (VIX) instead of the correct answer (IX)
if(roman.indexOf(romanKeys[index - 1]) > -1){
//remove the previous numeral we worked with
//and everything after it since we will replace them
roman.splice(roman.indexOf(romanKeys[index - 1]));
//push the current numeral and the one that appeared two iterations ago;
//think (IX) where we skip (V)
roman.push(romanKeys[index], romanKeys[index - 2]);
} else {
//else Example:(4) would attemt (IIII) so remove three I's and replace with a V
//to get the correct answer of (IV)
//remove the last 3 numerals which are all the same
roman.splice(-3);
//push the current numeral and the one that appeared right before it; think (IV)
roman.push(romanKeys[index], romanKeys[index - 1]);
}
}
//reduce our number by the value we already converted to a numeral
num -= curValue;
count++;
}
count = 1;
}
return roman.join("");
}
convertToRoman(36);
I know this is an old question but I'm pretty proud of this solution :) It only handles numbers less than 1000 but could easily be expanded to include however large you'd need by adding on to the 'numeralCodes' 2D array.
var numeralCodes = [["","I","II","III","IV","V","VI","VII","VIII","IX"], // Ones
["","X","XX","XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"], // Tens
["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"]]; // Hundreds
function convert(num) {
var numeral = "";
var digits = num.toString().split('').reverse();
for (var i=0; i < digits.length; i++){
numeral = numeralCodes[i][parseInt(digits[i])] + numeral;
}
return numeral;
}
<input id="text-input" type="text">
<button id="convert-button" onClick="var n = parseInt(document.getElementById('text-input').value);document.getElementById('text-output').value = convert(n);">Convert!</button>
<input id="text-output" style="display:block" type="text">
Loops may be more elegant but I find them hard to read. Came up with a more or less hard coded version that's easy on the eyes. As long as you understand the very first line, the rest is a no-brainer.
function romanNumeralGenerator (int) {
let roman = '';
roman += 'M'.repeat(int / 1000); int %= 1000;
roman += 'CM'.repeat(int / 900); int %= 900;
roman += 'D'.repeat(int / 500); int %= 500;
roman += 'CD'.repeat(int / 400); int %= 400;
roman += 'C'.repeat(int / 100); int %= 100;
roman += 'XC'.repeat(int / 90); int %= 90;
roman += 'L'.repeat(int / 50); int %= 50;
roman += 'XL'.repeat(int / 40); int %= 40;
roman += 'X'.repeat(int / 10); int %= 10;
roman += 'IX'.repeat(int / 9); int %= 9;
roman += 'V'.repeat(int / 5); int %= 5;
roman += 'IV'.repeat(int / 4); int %= 4;
roman += 'I'.repeat(int);
return roman;
}
I created two convert functions.
The first function can convert numbers to roman using reduce.
And the second function is very similar to the first function, the function uses the same way to convert the value.
Everything that you need to change is the _roman property. Because you have to extend this const with scale what you want, I place there max number 1000 but you can put more.
Larger scale with roman numbers you can find here https://www.tuomas.salste.net/doc/roman/numeri-romani.html
const _roman = { 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 };
// 1903 => MCMIII
function toRoman(number = 0) {
return Object.keys(_roman).reduce((acc, key) => {
while (number >= _roman[key]) {
acc += key;
number -= _roman[key];
}
return acc;
}, '');
}
// MCMIII => 1903
function fromRoman(roman = '') {
return Object.keys(_roman).reduce((acc, key) => {
while (roman.indexOf(key) === 0) {
acc += _roman[key];
roman = roman.substr(key.length);
}
return acc;
}, 0);
}
console.log(toRoman(1903)); // should return 'MCMIII
console.log(fromRoman('MCMIII')); // should return 1903
JavaScript
function romanize (num) {
if (!+num)
return false;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}
many other suggestions can be found at http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
Here is the solution with recursion, that looks simple:
const toRoman = (num, result = '') => {
const 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,
};
for (const key in map) {
if (num >= map[key]) {
if (num !== 0) {
return toRoman(num - map[key], result + key);
}
}
}
return result;
};
console.log(toRoman(402)); // CDII
console.log(toRoman(3000)); // MMM
console.log(toRoman(93)); // XCIII
console.log(toRoman(4)); // IV
This function will convert any number smaller than 3,999,999 to roman. Notice that numbers bigger than 3999 will be inside a label with text-decoration set to overline, this will add the overline that is the correct representation for x1000 when the number is bigger than 3999.
Four million (4,000,000) would be IV with two overlines so, you would need to use some trick to represent that, maybe a DIV with border-top, or some background image with those two overlines... Each overline represents x1000.
function convert(num){
num = parseInt(num);
if (num > 3999999) { alert('Number is too big!'); return false; }
if (num < 1) { alert('Number is too small!'); return false; }
var result = '',
ref = ['M','CM','D','CD','C','XC','L','XL','X','IX','V','IV','I'],
xis = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
if (num <= 3999999 && num >= 4000) {
num += ''; // need to convert to string for .substring()
result = '<label style="text-decoration: overline;">'+convert(num.substring(0,num.length-3))+'</label>';
num = num.substring(num.length-3);
}
for (x = 0; x < ref.length; x++){
while(num >= xis[x]){
result += ref[x];
num -= xis[x];
}
}
return result;
}
I created two twin arrays one with arabic numbers the other with the roman characters.
function convert(num) {
var result = '';
var rom = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
var ara = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
Then I added a cycle which scan the roman elements, adding the biggest still comprised in NUM to RESULT, then we decrease NUM of the same amount.
It is like we map a part of NUM in roman numbers and then we decrease it of the same amount.
for (var x = 0; x < rom.length; x++) {
while (num >= ara[x]) {
result += rom[x];
num -= ara[x];
}
}
return result;
}
If you want to convert a big number with more symbols, maybe this algo could help.
The only premise for symbols is that must be odd and follow the same rule (1, 5, 10, 50,100 ...., 10^(N)/2, 10^(N)).
var rnumbers = ["I","V","X","L","C","D","M"];
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border-top:1px solid black; padding:1px;">'+n+'</span> '}));
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border:1px solid black; border-bottom:1px none black; padding:1px;">'+n+'</span> '}));
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border-top:3px double black; padding:1px;">'+n+'</span> '}));
String.prototype.repeat = function( num ) {
return new Array( num + 1 ).join( this );
};
function toRoman(n) {
if(!n) return "";
var strn = new String(n);
var strnlength = strn.length;
var ret = "";
for(var i = 0 ; i < strnlength; i++) {
var index = strnlength*2 -2 - i*2;
var str;
var m = +strn[i];
if(index > rnumbers.length -1) {
str = rnumbers[rnumbers.length-1].repeat(m*Math.pow(10,Math.ceil((index-rnumbers.length)/2)));
}else {
str = rnumbers[index].repeat(m);
if (rnumbers.length >= index + 2) {
var rnregexp = rnumbers[index]
.split("(").join('\\(')
.split(")").join('\\)');
str = str.replace(new RegExp('(' + rnregexp + '){9}'), rnumbers[index] + rnumbers[index + 2])
.replace(new RegExp('(' + rnregexp + '){5}'), rnumbers[index + 1])
.replace(new RegExp('(' + rnregexp + '){4}'), rnumbers[index] + rnumbers[index + 1])
}
}
ret +=str;
}
return ret;
}
<input type="text" value="" onkeyup="document.getElementById('result').innerHTML = toRoman(this.value)"/>
<br/><br/>
<div id="result"></div>
After testing some of the implementations in this post, I have created a new optimized one in order to execute faster. The time execution is really low comparing with the others, but obviously the code is uglier :).
It could be even faster with an indexed array with all the posibilities.
Just in case it helps someone.
function concatNumLetters(letter, num) {
var text = "";
for(var i=0; i<num; i++){
text += letter;
}
return text;
}
function arabicToRomanNumber(arabic) {
arabic = parseInt(arabic);
var roman = "";
if (arabic >= 1000) {
var thousands = ~~(arabic / 1000);
roman = concatNumLetters("M", thousands);
arabic -= thousands * 1000;
}
if (arabic >= 900) {
roman += "CM";
arabic -= 900;
}
if (arabic >= 500) {
roman += "D";
arabic -= 500;
}
if (arabic >= 400) {
roman += "CD";
arabic -= 400;
}
if (arabic >= 100) {
var hundreds = ~~(arabic / 100);
roman += concatNumLetters("C", hundreds);
arabic -= hundreds * 100;
}
if (arabic >= 90) {
roman += "XC";
arabic -= 90;
}
if (arabic >= 50) {
roman += "L";
arabic -= 50;
}
if (arabic >= 40) {
roman += "XL";
arabic -= 40;
}
if (arabic >= 10) {
var dozens = ~~(arabic / 10);
roman += concatNumLetters("X", dozens);
arabic -= dozens * 10;
}
if (arabic >= 9) {
roman += "IX";
arabic -= 9;
}
if (arabic >= 5) {
roman += "V";
arabic -= 5;
}
if (arabic >= 4) {
roman += "IV";
arabic -= 4;
}
if (arabic >= 1) {
roman += concatNumLetters("I", arabic);
}
return roman;
}
function convertToRoman(num) {
var roman = {
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 result = '';
for (var key in roman) {
if (num == roman[key]) {
return result +=key;
}
var check = num > roman[key];
if(check) {
result = result + key.repeat(parseInt(num/roman[key]));
num = num%roman[key];
}
}
return result;
}
console.log(convertToRoman(36));
I didn't see this posted already so here's an interesting solution using only string manipulation:
var numbers = [1, 4, 5, 7, 9, 14, 15, 19, 20, 44, 50, 94, 100, 444, 500, 659, 999, 1000, 1024];
var romanNumeralGenerator = function (number) {
return 'I'
.repeat(number)
.replace(/I{5}/g, 'V')
.replace(/V{2}/g, 'X')
.replace(/X{5}/g, 'L')
.replace(/L{2}/g, 'C')
.replace(/C{5}/g, 'D')
.replace(/D{2}/g, 'M')
.replace(/DC{4}/g, 'CM')
.replace(/C{4}/g, 'CD')
.replace(/LX{4}/g, 'XC')
.replace(/X{4}/g, 'XL')
.replace(/VI{4}/g, 'IX')
.replace(/I{4}/g, 'IV')
};
console.log(numbers.map(romanNumeralGenerator))
This function works on the the different character sets in each digit. To add another digit add the roman numeral string the 1 place, 5 place and next 1 place. This is nice because you update it with only knowing the next set of characters used.
function toRoman(n){
var d=0,o="",v,k="IVXLCDM".split("");
while(n!=0){
v=n%10,x=k[d],y=k[d+1],z=k[d+2];
o=["",x,x+x,x+x+x,x+y,y,y+x,y+x+x,y+x+x+x,x+z][v]+o;
n=(n-v)/10,d+=2;
}
return o
}
var out = "";
for (var i = 0; i < 100; i++) {
out += toRoman(i) + "\n";
}
document.getElementById("output").innerHTML = out;
<pre id="output"></pre>
function convertToRoman(num) {
var romans = {
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'
};
var popped, rem, roman = '',
keys = Object.keys(romans);
while (num > 0) {
popped = keys.pop();
m = Math.floor(num / popped);
num = num % popped;
console.log('popped:', popped, ' m:', m, ' num:', num, ' roman:', roman);
while (m-- > 0) {
roman += romans[popped];
}
while (num / popped === 0) {
popped = keys.pop();
delete romans[popped];
}
}
return roman;
}
var result = convertToRoman(3999);
console.log(result);
document.getElementById('roman').innerHTML = 'Roman: ' + result;
p {
color: darkblue;
}
<p>Decimal: 3999</p>
<p id="roman">Roman:</p>
I just made this at freecodecamp. It can easily be expanded.
function convertToRoman(num) {
var roman ="";
var values = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
var literals = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"];
for(i=0;i<values.length;i++){
if(num>=values[i]){
if(5<=num && num<=8) num -= 5;
else if(1<=num && num<=3) num -= 1;
else num -= values[i];
roman += literals[i];
i--;
}
}
return roman;
}
Here's a regular expression solution:
function deromanize(roman) {
var r = 0;
// regular expressions to check if valid Roman Number.
if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(roman))
throw new Error('Invalid Roman Numeral.');
roman.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) {
r += {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}[i];
});
return r;
}
I really liked the solution by jaggedsoft but I couldn't reply because my rep is TOO LOW :( :(
I broke it down to explain it a little bit for those that don't understand it. Hopefully it helps someone.
function convertToRoman(num) {
var lookup =
{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},roman = '',i;
for ( i in lookup ) {
while ( num >= lookup[i] ) { //while input is BIGGGER than lookup #..1000, 900, 500, etc.
roman += i; //roman is set to whatever i is (M, CM, D, CD...)
num -= lookup[i]; //takes away the first num it hits that is less than the input
//in this case, it found X:10, added X to roman, then took away 10 from input
//input lowered to 26, X added to roman, repeats and chips away at input number
//repeats until num gets down to 0. This triggers 'while' loop to stop.
}
}
return roman;
}
console.log(convertToRoman(36));
IF this number in HTMLElement (such as span), we recommend to Add HTML attribute data-format :
Number.prototype.toRoman = function() {
var e = Math.floor(this),
t, n = "",
i = 3999,
s = 0;
v = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], r = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
if (e < 1 || e > i) return "";
while (s < 13) {
t = v[s];
while (e >= t) {
e -= t;
n += r[s]
}
if (e == 0) return n;
++s
}
return ""
};
var fnrom = function(e) {
if (parseInt(e.innerHTML)) {
e.innerHTML = parseInt(e.innerHTML).toRoman()
}
};
setTimeout(function() {
[].forEach.call(document.querySelectorAll("[data-format=roman]"), fnrom)
}, 10)
Phase <span data-format="roman">4</span> Sales
function convertToRoman(num) {
let roman = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
let arabic = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
let index = 0;
let result = "";
while (num > 0) {
if (num >= arabic[index]) {
result += roman[index];
num -= arabic[index];
} else index++;
}
return result;
}
/*my beginner-nooby solution for numbers 1-999 :)*/
function convert(num) {
var RomNumDig = [['','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']];
var lastDig = num%10;
var ourNumb1 = RomNumDig[0][lastDig]||'';
if(num>=10) {
var decNum = (num - lastDig)/10;
if(decNum>9)decNum%=10;
var ourNumb2 = RomNumDig[1][decNum-1]||'';}
if(num>=100) {
var hundNum = ((num-num%100)/100);
var ourNumb3 = RomNumDig[2][hundNum-1]||'';}
return ourNumb3+ourNumb2+ourNumb1;
}
console.log(convert(950));//CML
/*2nd my beginner-nooby solution for numbers 1-10, but it can be easy transformed for larger numbers :)*/
function convert(num) {
var ourNumb = '';
var romNumDig = ['I','IV','V','IX','X'];
var decNum = [1,4,5,9,10];
for (var i=decNum.length-1; i>0; i--) {
while(num>=decNum[i]) {
ourNumb += romNumDig[i];
num -= decNum[i];
}
}
return ourNumb;
}
console.log(convert(9));//IX
function toRoman(n) {
var decimals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
var roman = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
for (var i = 0; i < decimals.length; i++) {
if(n < 1)
return "";
if(n >= decimals[i]) {
return roman[i] + toRoman(n - decimals[i]);
}
}
}
This works for all numbers only in need of roman numerals M and below.
function convert(num) {
var code = [
[1000, "M"], [900, "CM"], [800, "DCCC"], [700, "DCC"], [600, "DC"],
[500, "D"], [400, "CD"], [300, "CCC"], [200, "CC"],
[100, "C"], [90, "XC"], [80, "LXXX"], [70, "LXX"], [60, "LX"],
[50, "L"], [40, "XL"], [30, "XXX"], [20, "XX"],
[10, "X"], [9, "IX"], [8, "VIII"], [7, "VII"], [6, "VI"],
[5, "V"], [4, "IV"], [3, "III"], [2, "II"], [1, "I"],
];
var rom = "";
for(var i=0; i<code.length; i++) {
while(num >= code[i][0]) {
rom += code[i][1];
num -= code[i][0];
}
}
return rom;
}
This is the first time I really got stuck on freecodecamp. I perused through some solutions here and was amazed at how different they all were. Here is what ended up working for me.
function convertToRoman(num) {
var roman = "";
var lookupObj = {
1000:"M",
900:"CM",
500:"D",
400:"CD",
100:"C",
90:"XC",
50:"L",
40:"XL",
10:"X",
9:"IX",
4:"IV",
5:"V",
1:"I",
};
var arrayLen = Object.keys(lookupObj).length;
while(num>0){
for (i=arrayLen-1 ; i>=0 ; i--){
if(num >= Object.keys(lookupObj)[i]){
roman = roman + lookupObj[Object.keys(lookupObj)[i]];
num = num - Object.keys(lookupObj)[i];
break;
}
}
}
return roman;
}
convertToRoman(1231);
function convertToRoman(num) {
var romNumerals = [["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 runningTotal = 0;
var roman = "";
for (var i = 0; i < romNumerals.length; i++) {
while (runningTotal + romNumerals[i][1] <= num) {
runningTotal += romNumerals[i][1];
roman += romNumerals[i][0];
}
}
return roman;
}

Recursive Integer To Roman Numeral Funciton Going Crazy At The End

I am trying to get an integer to Roman Numeral function to work. It does work, but what it's returning gets so weird at the end. I know that it has something to do with recursion, but I can't tell what.
the returned value at the end goes from
MMCCCXXVIII -
MMCCCXXVII -
MMCCCXXVI -
MMCCCXXV -
MMCCCXX -
MMCCCX -
MMCCC -
MMCC-
MM-
M
function romanToInt(int, rom = '') {
function binarySearch(arr, q, rom, le, ri) {
// set left right middle
let l = le || 0,
r = ri || arr.length - 1,
m = Math.floor((r + l) / 2);
// check if the middle is q
if (arr[m] === q) {
return m;
}
if (arr[l] === q) {
return l;
}
if (arr[r] === q) {
return r;
}
if (q > arr[m] && m + 1 < r) {
return binarySearch(arr, q, rom, m, ri);
}
if (q < arr[m] && m - 1 > l) {
return binarySearch(arr, q, rom, le, m);
}
let numToUse = '0';
let lTemp = 500000000000000000000000000000,
rTemp = 500000000000000000000000000000,
mTemp = 500000000000000000000000000000;
if (arr[l] - q < 0) {
lTemp = Math.abs(arr[l] - q);
}
if (arr[m] - q < 0) {
mTemp = Math.abs(arr[m] - q);
}
if (arr[r] - q < 0) {
rTemp = Math.abs(arr[r] - q);
}
let tempToFind = Math.min(lTemp, mTemp, rTemp);
if (lTemp === tempToFind) {
return l;
}
if (rTemp === tempToFind) {
return r;
}
if (mTemp === tempToFind) {
return m;
}
return `pending`;
}
let convArr = [
[1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000],
['I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M'],
],
romanNumeral = rom;
let returnedVal = binarySearch(convArr[0], int);
romanNumeral = romanNumeral + convArr[1][returnedVal];
let newQ = int - convArr[0][returnedVal];
if (newQ === 0) {
return romanNumeral;
} else {
romanToInt(newQ, romanNumeral);
}
return romanNumeral;
}
const rom = 2328;
console.log(romanToInt(rom));
You forgot to return the recursive return value.
if (newQ === 0) {
return romanNumeral;
} else {
romanToInt(newQ, romanNumeral);
}
return romanNumeral;
Should be:
if (newQ === 0) {
return romanNumeral;
} else {
return romanToInt(newQ, romanNumeral); // <- return the recursive result
}
// or use a guard clause
if (newQ === 0) return romanNumeral;
return romanToInt(newQ, romanNumeral)
function romanToInt(int, rom = '') {
function binarySearch(arr, q, rom, le, ri) {
// set left right middle
let l = le || 0,
r = ri || arr.length - 1,
m = Math.floor((r + l) / 2);
// check if the middle is q
if (arr[m] === q) {
return m;
}
if (arr[l] === q) {
return l;
}
if (arr[r] === q) {
return r;
}
if (q > arr[m] && m + 1 < r) {
return binarySearch(arr, q, rom, m, ri);
}
if (q < arr[m] && m - 1 > l) {
return binarySearch(arr, q, rom, le, m);
}
let numToUse = '0';
let lTemp = 500000000000000000000000000000,
rTemp = 500000000000000000000000000000,
mTemp = 500000000000000000000000000000;
if (arr[l] - q < 0) {
lTemp = Math.abs(arr[l] - q);
}
if (arr[m] - q < 0) {
mTemp = Math.abs(arr[m] - q);
}
if (arr[r] - q < 0) {
rTemp = Math.abs(arr[r] - q);
}
let tempToFind = Math.min(lTemp, mTemp, rTemp);
if (lTemp === tempToFind) {
return l;
}
if (rTemp === tempToFind) {
return r;
}
if (mTemp === tempToFind) {
return m;
}
return `pending`;
}
let convArr = [
[1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000],
['I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M'],
],
romanNumeral = rom;
let returnedVal = binarySearch(convArr[0], int);
romanNumeral = romanNumeral + convArr[1][returnedVal];
let newQ = int - convArr[0][returnedVal];
if (newQ === 0) {
return romanNumeral;
} else {
return romanToInt(newQ, romanNumeral); // <- return the recursive result
}
}
const rom = 2328;
console.log(romanToInt(rom));
Not sure about the binary search. This is a slightly different recursive approach,.
const numberToRoman = (num, rom = "") => {
if (num === 0) return rom;
for (var i = convArr[0].length - 1; i >= 0; i--) {
if (num >= convArr[0][i]) {
return numberToRoman(num - convArr[0][i], rom + convArr[1][i]);
}
}
};
const convArr = [
[1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000],
['I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M'],
];
const someNumber = 2328;
console.log(numberToRoman(someNumber));

How can I skip if number===0?

When converting numbers to roman numerals, I need if the first number "2000" is larger than what is on the array provided, add the maximum value of the array until it comes to 2000.
I run into a problem after it is done putting out the numerals for 2000. It goes to the next number in the array, which is ZERO! [2000,0,80,4]
I need to skip if it is zero. I attempted a finalnum[i]!=0.
for (i = 0; i < finalnum.length; i++) {
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
}
var romanNum = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
var romanEquiv = ['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'];
function convertToRoman(num) {
//convert num to separate numbers
var indices = [];
var romans = [];
//split out the number first
var nextint = num.toString().split("");
var nextarr = [];
for (var i = 0; i < nextint.length; i++) {
var firstnum = "0".repeat(nextint.length - i - 1);
nextarr.push(nextint[i] + firstnum);
var finalnum = nextarr.map(Number);
}
//find the romannumerals
for (i = 0; i < finalnum.length; i++) {
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
return romans.join('');
}
console.log(convertToRoman(2084));
Test if finalnum[i] == 0, and skip to the next number with continue;.
However, the rest of your code doesn't seem to work correctly. It still just returns "MM". There are other problems in your algorithm that
You should also use return in the function. Do the console.log() in the caller.
BTW, the reduce loop can be replaced with:
var max = Math.max.apply(Math, romanNum);
I'm not really sure why you need this variable, and you certainly don't need to calculate it each time through the loop, since the romanNum array never changes. It seems to be part of your confused algorithm. There are lots of other questions on SO (and also Code Review) about converting to Roman numerals, you can look at them.
var romanNum = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000];
var romanEquiv = ['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'];
function convertToRoman(num) {
//convert num to separate numbers
var indices = [];
var romans = [];
//split out the number first
var nextint = num.toString().split("");
var nextarr = [];
for (var i = 0; i < nextint.length; i++) {
var firstnum = "0".repeat(nextint.length - i - 1);
nextarr.push(nextint[i] + firstnum);
var finalnum = nextarr.map(Number);
}
//find the romannumerals
for (i = 0; i < finalnum.length; i++) {
if (finalnum[i] == 0) { // skip zeroes
continue;
}
var idx = romanNum.indexOf(finalnum[i]);
if (idx === -1 && finalnum[i] != 0) {
var max = romanNum.reduce(function(a, b) {
return (Math.max(a, b));
});
var amountoftimes = finalnum[i] / max;
idx = romanNum.indexOf(max);
romans.push(romanEquiv[idx].repeat(amountoftimes));
} //end of if statement
}
return romans.join('');
}
console.log(convertToRoman(2084));

Converting to Roman Numerals in Javascript - Weird bug

function convertToRoman(num) {
var thisMap = {
1:[1],
2:[1, 1],
3:[1, 1, 1],
4:[1, 5],
5:[5],
6:[5, 1],
7:[5, 1, 1],
8:[5, 1, 1, 1],
9:[1, 10],
0:[0]
};
var numMap = {
1000:"M",
500:"D",
100:"C",
50:"L",
10:"X",
5:"V",
1:"I"
};
numArr = num.toString().split("");
var thisIndex = 1;
var tallyArr = [];
for (var i = numArr.length - 1; i >= 0; i--) {
tallyArr.unshift(thisMap[numArr[i]]);
}
thisIndex = Math.pow(10, tallyArr.length - 1);
checkArr = [];
<<<BUG HERE>>>
for (var x = 0; x < tallyArr.length; x++) {
for (var y = 0; y < tallyArr[x].length; y++) {
tallyArr[x][y] *= thisIndex;
}
thisIndex = thisIndex / 10;
}
<<</BUG HERE>>>
var finalArr = [];
for (var a = 0; a < tallyArr.length; a++) {
for (var b = 0; b < tallyArr[a].length; b++) {
finalArr.push(numMap[tallyArr[a][b]]);
}
}
finalAnswer = finalArr.join("");
return finalAnswer;
}
convertToRoman(88);
So this is my function for converting a number into a Roman Numeral in Javascript. It basically formats every number into the right format using thisMap, then uses thisIndex to multiply by either 1000, 100 or 10, and then compares to numMap to get the correct Roman Numeral.
It seems to work in most of the test cases, except with 44, 99, or 3999.
In these cases, it seems to multiply the numbers by the wrong amount, so 44 becomes XLXL, when it should be XLIV.
I think the bug is between the <<>> tags I've inserted, because that is where the numbers seem to be multiplied wrong.
However, I can't spot the problem.
Thanks.
I tried a different approach.
function convertToRoman(num) {
let arabicArray = [ 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000, 4000, 5000, 5001]
let romanArray = ['I', 'IV', 'V', 'IX', 'X', 'XL', 'L', 'XC', 'C', 'CD', 'D', 'CM', 'M', 'MV', 'V', 'limit of 5000']
let roman = ""
loop()
function loop() {
for (let i = 0; i < arabicArray.length; i++) {
if (num < arabicArray[i]) {
roman += romanArray[i - 1]
num -= arabicArray[i - 1]
while (num != 0) {loop()} break;
}
}
}
return roman
}
console.log(convertToRoman(3))
However this gives you a limit to 5000.
Try this: x loop should run through all the length of tallyArr except the last one.
function convertToRoman(num) {
// ... code ...
for (var x = 0; x < tallyArr.length - 1; x++) {
for (var y = 0; y < tallyArr[x].length; y++) {
tallyArr[x][y] *= thisIndex;
}
thisIndex = thisIndex / 10;
}
// ... more code ...
}
Your solution seems a bit over engineered and overly complicated sometimes simpler is better and what might seem like a clever answer and looking for the overly eloquent solution can trip you up.
function convertToRoman(num) {
var output = "";
var numMap = [
{ limit: 1000, value: "M" },
{ limit: 900, value: "CM" },
{ limit: 500, value: "D" },
{ limit: 400, value: "CD" },
{ limit: 100, value: "C" },
{ limit: 90, value: "XC" },
{ limit: 50, value: "L" },
{ limit: 40, value: "XL" },
{ limit: 10, value: "X" },
{ limit: 9, value: "IX" },
{ limit: 5, value: "V" },
{ limit: 4, value: "IV" },
{ limit: 1, value: "I" }
];
for(var index = 0; index < numMap.length; index++) {
var value = numMap[index].value,
limit = numMap[index].limit;
while(num >= limit) {
output += value;
num -= limit;
}
}
return output;
}
alert(convertToRoman(1));
alert(convertToRoman(4));
alert(convertToRoman(5));
alert(convertToRoman(9));
alert(convertToRoman(10));
alert(convertToRoman(88));
alert(convertToRoman(2016));
JSFiddle
var romanNumber = [
[1, 'I'], [2, 'II'], [3, 'III'],[4, 'IV'],
[5, 'V'], [6, 'VI'],[7, 'VII'], [8, 'VIII'],
[9, 'IX'],[10, 'X']
];
function convertToRoman(number) {
if (number === 0) {
return '';
}
for (var i = 0; i < romanNumber.length; i++) {
if (number === romanNumber[i][0]) {
return romanNumber[i][1];
}
}
}
console.log(convertToRoman(1));
console.log(convertToRoman(2));
console.log(convertToRoman(3));
console.log(convertToRoman(4));
console.log(convertToRoman(5));
console.log(convertToRoman(6));
console.log(convertToRoman(7));
console.log(convertToRoman(8));
console.log(convertToRoman(9));
console.log(convertToRoman(10));

Convert a number into a Roman numeral in JavaScript

How can I convert integers into roman numerals?
function romanNumeralGenerator (int) {
}
For example, see the following sample inputs and outputs:
1 = "I"
5 = "V"
10 = "X"
20 = "XX"
3999 = "MMMCMXCIX"
Caveat: Only support numbers between 1 and 3999
There is a nice one here on this blog I found using google:
http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
function romanize (num) {
if (isNaN(num))
return NaN;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}
function romanize(num) {
var lookup = {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},roman = '',i;
for ( i in lookup ) {
while ( num >= lookup[i] ) {
roman += i;
num -= lookup[i];
}
}
return roman;
}
Reposted from a 2008 comment located at: http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
VIEW DEMO
I don't understand why everyone's solution is so long and uses multiple for loops.
function convertToRoman(num) {
var roman = {
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 str = '';
for (var i of Object.keys(roman)) {
var q = Math.floor(num / roman[i]);
num -= q * roman[i];
str += i.repeat(q);
}
return str;
}
I've developed the recursive solution below. The function returns one letter and then calls itself to return the next letter. It does it until the number passed to the function is 0 which means that all letters have been found and we can exit the recursion.
var romanMatrix = [
[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']
];
function convertToRoman(num) {
if (num === 0) {
return '';
}
for (var i = 0; i < romanMatrix.length; i++) {
if (num >= romanMatrix[i][0]) {
return romanMatrix[i][1] + convertToRoman(num - romanMatrix[i][0]);
}
}
}
These functions convert any positive whole number to its equivalent Roman Numeral string; and any Roman Numeral to its number.
Number to Roman Numeral:
Number.prototype.toRoman= function () {
var num = Math.floor(this),
val, s= '', i= 0,
v = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1],
r = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
function toBigRoman(n) {
var ret = '', n1 = '', rem = n;
while (rem > 1000) {
var prefix = '', suffix = '', n = rem, s = '' + rem, magnitude = 1;
while (n > 1000) {
n /= 1000;
magnitude *= 1000;
prefix += '(';
suffix += ')';
}
n1 = Math.floor(n);
rem = s - (n1 * magnitude);
ret += prefix + n1.toRoman() + suffix;
}
return ret + rem.toRoman();
}
if (this - num || num < 1) num = 0;
if (num > 3999) return toBigRoman(num);
while (num) {
val = v[i];
while (num >= val) {
num -= val;
s += r[i];
}
++i;
}
return s;
};
Roman Numeral string to Number:
Number.fromRoman = function (roman, accept) {
var s = roman.toUpperCase().replace(/ +/g, ''),
L = s.length, sum = 0, i = 0, next, val,
R = { M: 1000, D: 500, C: 100, L: 50, X: 10, V: 5, I: 1 };
function fromBigRoman(rn) {
var n = 0, x, n1, S, rx =/(\(*)([MDCLXVI]+)/g;
while ((S = rx.exec(rn)) != null) {
x = S[1].length;
n1 = Number.fromRoman(S[2])
if (isNaN(n1)) return NaN;
if (x) n1 *= Math.pow(1000, x);
n += n1;
}
return n;
}
if (/^[MDCLXVI)(]+$/.test(s)) {
if (s.indexOf('(') == 0) return fromBigRoman(s);
while (i < L) {
val = R[s.charAt(i++)];
next = R[s.charAt(i)] || 0;
if (next - val > 0) val *= -1;
sum += val;
}
if (accept || sum.toRoman() === s) return sum;
}
return NaN;
};
I personally think the neatest way (not by any means the fastest) is with recursion.
function convert(num) {
if(num < 1){ return "";}
if(num >= 40){ return "XL" + convert(num - 40);}
if(num >= 10){ return "X" + convert(num - 10);}
if(num >= 9){ return "IX" + convert(num - 9);}
if(num >= 5){ return "V" + convert(num - 5);}
if(num >= 4){ return "IV" + convert(num - 4);}
if(num >= 1){ return "I" + convert(num - 1);}
}
console.log(convert(39));
//Output: XXXIX
This will only support numbers 1-40, but it can easily be extended by following the pattern.
This version does not require any hard coded logic for edge cases such as 4(IV),9(IX),40(XL),900(CM), etc. as the others do.
I have tested this code against a data set from 1-3999 and it works.
TLDR;
This also means this solution can handle numbers greater than the maximum roman scale could (3999).
It appears there is an alternating rule for deciding the next major roman numeral character. Starting with I multiply by 5 to get the next numeral V and then by 2 to get X, then by 5 to get L, and then by 2 to get C, etc to get the next major numeral character in the scale. In this case lets assume "T" gets added to the scale to allow for larger numbers than 3999 which the original roman scale allows. In order to maintain the same algorithm "T" would represent 5000.
I = 1
V = I * 5
X = V * 2
L = X * 5
C = L * 2
D = C * 5
M = D * 2
T = M * 5
This could then allow us to represent numbers from 4000 to 5000; MT = 4000 for example.
Code:
function convertToRoman(num) {
//create key:value pairs
var romanLookup = {M:1000, D:500, C:100, L:50, X:10, V:5, I:1};
var roman = [];
var romanKeys = Object.keys(romanLookup);
var curValue;
var index;
var count = 1;
for(var numeral in romanLookup){
curValue = romanLookup[numeral];
index = romanKeys.indexOf(numeral);
while(num >= curValue){
if(count < 4){
//push up to 3 of the same numeral
roman.push(numeral);
} else {
//else we had to push four, so we need to convert the numerals
//to the next highest denomination "coloring-up in poker speak"
//Note: We need to check previous index because it might be part of the current number.
//Example:(9) would attempt (VIIII) so we would need to remove the V as well as the I's
//otherwise removing just the last three III would be incorrect, because the swap
//would give us (VIX) instead of the correct answer (IX)
if(roman.indexOf(romanKeys[index - 1]) > -1){
//remove the previous numeral we worked with
//and everything after it since we will replace them
roman.splice(roman.indexOf(romanKeys[index - 1]));
//push the current numeral and the one that appeared two iterations ago;
//think (IX) where we skip (V)
roman.push(romanKeys[index], romanKeys[index - 2]);
} else {
//else Example:(4) would attemt (IIII) so remove three I's and replace with a V
//to get the correct answer of (IV)
//remove the last 3 numerals which are all the same
roman.splice(-3);
//push the current numeral and the one that appeared right before it; think (IV)
roman.push(romanKeys[index], romanKeys[index - 1]);
}
}
//reduce our number by the value we already converted to a numeral
num -= curValue;
count++;
}
count = 1;
}
return roman.join("");
}
convertToRoman(36);
I know this is an old question but I'm pretty proud of this solution :) It only handles numbers less than 1000 but could easily be expanded to include however large you'd need by adding on to the 'numeralCodes' 2D array.
var numeralCodes = [["","I","II","III","IV","V","VI","VII","VIII","IX"], // Ones
["","X","XX","XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"], // Tens
["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM"]]; // Hundreds
function convert(num) {
var numeral = "";
var digits = num.toString().split('').reverse();
for (var i=0; i < digits.length; i++){
numeral = numeralCodes[i][parseInt(digits[i])] + numeral;
}
return numeral;
}
<input id="text-input" type="text">
<button id="convert-button" onClick="var n = parseInt(document.getElementById('text-input').value);document.getElementById('text-output').value = convert(n);">Convert!</button>
<input id="text-output" style="display:block" type="text">
Loops may be more elegant but I find them hard to read. Came up with a more or less hard coded version that's easy on the eyes. As long as you understand the very first line, the rest is a no-brainer.
function romanNumeralGenerator (int) {
let roman = '';
roman += 'M'.repeat(int / 1000); int %= 1000;
roman += 'CM'.repeat(int / 900); int %= 900;
roman += 'D'.repeat(int / 500); int %= 500;
roman += 'CD'.repeat(int / 400); int %= 400;
roman += 'C'.repeat(int / 100); int %= 100;
roman += 'XC'.repeat(int / 90); int %= 90;
roman += 'L'.repeat(int / 50); int %= 50;
roman += 'XL'.repeat(int / 40); int %= 40;
roman += 'X'.repeat(int / 10); int %= 10;
roman += 'IX'.repeat(int / 9); int %= 9;
roman += 'V'.repeat(int / 5); int %= 5;
roman += 'IV'.repeat(int / 4); int %= 4;
roman += 'I'.repeat(int);
return roman;
}
I created two convert functions.
The first function can convert numbers to roman using reduce.
And the second function is very similar to the first function, the function uses the same way to convert the value.
Everything that you need to change is the _roman property. Because you have to extend this const with scale what you want, I place there max number 1000 but you can put more.
Larger scale with roman numbers you can find here https://www.tuomas.salste.net/doc/roman/numeri-romani.html
const _roman = { 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 };
// 1903 => MCMIII
function toRoman(number = 0) {
return Object.keys(_roman).reduce((acc, key) => {
while (number >= _roman[key]) {
acc += key;
number -= _roman[key];
}
return acc;
}, '');
}
// MCMIII => 1903
function fromRoman(roman = '') {
return Object.keys(_roman).reduce((acc, key) => {
while (roman.indexOf(key) === 0) {
acc += _roman[key];
roman = roman.substr(key.length);
}
return acc;
}, 0);
}
console.log(toRoman(1903)); // should return 'MCMIII
console.log(fromRoman('MCMIII')); // should return 1903
Here is the solution with recursion, that looks simple:
const toRoman = (num, result = '') => {
const 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,
};
for (const key in map) {
if (num >= map[key]) {
if (num !== 0) {
return toRoman(num - map[key], result + key);
}
}
}
return result;
};
console.log(toRoman(402)); // CDII
console.log(toRoman(3000)); // MMM
console.log(toRoman(93)); // XCIII
console.log(toRoman(4)); // IV
JavaScript
function romanize (num) {
if (!+num)
return false;
var digits = String(+num).split(""),
key = ["","C","CC","CCC","CD","D","DC","DCC","DCCC","CM",
"","X","XX","XXX","XL","L","LX","LXX","LXXX","XC",
"","I","II","III","IV","V","VI","VII","VIII","IX"],
roman = "",
i = 3;
while (i--)
roman = (key[+digits.pop() + (i * 10)] || "") + roman;
return Array(+digits.join("") + 1).join("M") + roman;
}
many other suggestions can be found at http://blog.stevenlevithan.com/archives/javascript-roman-numeral-converter
This function will convert any number smaller than 3,999,999 to roman. Notice that numbers bigger than 3999 will be inside a label with text-decoration set to overline, this will add the overline that is the correct representation for x1000 when the number is bigger than 3999.
Four million (4,000,000) would be IV with two overlines so, you would need to use some trick to represent that, maybe a DIV with border-top, or some background image with those two overlines... Each overline represents x1000.
function convert(num){
num = parseInt(num);
if (num > 3999999) { alert('Number is too big!'); return false; }
if (num < 1) { alert('Number is too small!'); return false; }
var result = '',
ref = ['M','CM','D','CD','C','XC','L','XL','X','IX','V','IV','I'],
xis = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
if (num <= 3999999 && num >= 4000) {
num += ''; // need to convert to string for .substring()
result = '<label style="text-decoration: overline;">'+convert(num.substring(0,num.length-3))+'</label>';
num = num.substring(num.length-3);
}
for (x = 0; x < ref.length; x++){
while(num >= xis[x]){
result += ref[x];
num -= xis[x];
}
}
return result;
}
I created two twin arrays one with arabic numbers the other with the roman characters.
function convert(num) {
var result = '';
var rom = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
var ara = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
Then I added a cycle which scan the roman elements, adding the biggest still comprised in NUM to RESULT, then we decrease NUM of the same amount.
It is like we map a part of NUM in roman numbers and then we decrease it of the same amount.
for (var x = 0; x < rom.length; x++) {
while (num >= ara[x]) {
result += rom[x];
num -= ara[x];
}
}
return result;
}
If you want to convert a big number with more symbols, maybe this algo could help.
The only premise for symbols is that must be odd and follow the same rule (1, 5, 10, 50,100 ...., 10^(N)/2, 10^(N)).
var rnumbers = ["I","V","X","L","C","D","M"];
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border-top:1px solid black; padding:1px;">'+n+'</span> '}));
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border:1px solid black; border-bottom:1px none black; padding:1px;">'+n+'</span> '}));
rnumbers = rnumbers.concat(["V","X","L","C","D","M"].map(function(n) {return '<span style="border-top:3px double black; padding:1px;">'+n+'</span> '}));
String.prototype.repeat = function( num ) {
return new Array( num + 1 ).join( this );
};
function toRoman(n) {
if(!n) return "";
var strn = new String(n);
var strnlength = strn.length;
var ret = "";
for(var i = 0 ; i < strnlength; i++) {
var index = strnlength*2 -2 - i*2;
var str;
var m = +strn[i];
if(index > rnumbers.length -1) {
str = rnumbers[rnumbers.length-1].repeat(m*Math.pow(10,Math.ceil((index-rnumbers.length)/2)));
}else {
str = rnumbers[index].repeat(m);
if (rnumbers.length >= index + 2) {
var rnregexp = rnumbers[index]
.split("(").join('\\(')
.split(")").join('\\)');
str = str.replace(new RegExp('(' + rnregexp + '){9}'), rnumbers[index] + rnumbers[index + 2])
.replace(new RegExp('(' + rnregexp + '){5}'), rnumbers[index + 1])
.replace(new RegExp('(' + rnregexp + '){4}'), rnumbers[index] + rnumbers[index + 1])
}
}
ret +=str;
}
return ret;
}
<input type="text" value="" onkeyup="document.getElementById('result').innerHTML = toRoman(this.value)"/>
<br/><br/>
<div id="result"></div>
After testing some of the implementations in this post, I have created a new optimized one in order to execute faster. The time execution is really low comparing with the others, but obviously the code is uglier :).
It could be even faster with an indexed array with all the posibilities.
Just in case it helps someone.
function concatNumLetters(letter, num) {
var text = "";
for(var i=0; i<num; i++){
text += letter;
}
return text;
}
function arabicToRomanNumber(arabic) {
arabic = parseInt(arabic);
var roman = "";
if (arabic >= 1000) {
var thousands = ~~(arabic / 1000);
roman = concatNumLetters("M", thousands);
arabic -= thousands * 1000;
}
if (arabic >= 900) {
roman += "CM";
arabic -= 900;
}
if (arabic >= 500) {
roman += "D";
arabic -= 500;
}
if (arabic >= 400) {
roman += "CD";
arabic -= 400;
}
if (arabic >= 100) {
var hundreds = ~~(arabic / 100);
roman += concatNumLetters("C", hundreds);
arabic -= hundreds * 100;
}
if (arabic >= 90) {
roman += "XC";
arabic -= 90;
}
if (arabic >= 50) {
roman += "L";
arabic -= 50;
}
if (arabic >= 40) {
roman += "XL";
arabic -= 40;
}
if (arabic >= 10) {
var dozens = ~~(arabic / 10);
roman += concatNumLetters("X", dozens);
arabic -= dozens * 10;
}
if (arabic >= 9) {
roman += "IX";
arabic -= 9;
}
if (arabic >= 5) {
roman += "V";
arabic -= 5;
}
if (arabic >= 4) {
roman += "IV";
arabic -= 4;
}
if (arabic >= 1) {
roman += concatNumLetters("I", arabic);
}
return roman;
}
function convertToRoman(num) {
var roman = {
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 result = '';
for (var key in roman) {
if (num == roman[key]) {
return result +=key;
}
var check = num > roman[key];
if(check) {
result = result + key.repeat(parseInt(num/roman[key]));
num = num%roman[key];
}
}
return result;
}
console.log(convertToRoman(36));
I didn't see this posted already so here's an interesting solution using only string manipulation:
var numbers = [1, 4, 5, 7, 9, 14, 15, 19, 20, 44, 50, 94, 100, 444, 500, 659, 999, 1000, 1024];
var romanNumeralGenerator = function (number) {
return 'I'
.repeat(number)
.replace(/I{5}/g, 'V')
.replace(/V{2}/g, 'X')
.replace(/X{5}/g, 'L')
.replace(/L{2}/g, 'C')
.replace(/C{5}/g, 'D')
.replace(/D{2}/g, 'M')
.replace(/DC{4}/g, 'CM')
.replace(/C{4}/g, 'CD')
.replace(/LX{4}/g, 'XC')
.replace(/X{4}/g, 'XL')
.replace(/VI{4}/g, 'IX')
.replace(/I{4}/g, 'IV')
};
console.log(numbers.map(romanNumeralGenerator))
This function works on the the different character sets in each digit. To add another digit add the roman numeral string the 1 place, 5 place and next 1 place. This is nice because you update it with only knowing the next set of characters used.
function toRoman(n){
var d=0,o="",v,k="IVXLCDM".split("");
while(n!=0){
v=n%10,x=k[d],y=k[d+1],z=k[d+2];
o=["",x,x+x,x+x+x,x+y,y,y+x,y+x+x,y+x+x+x,x+z][v]+o;
n=(n-v)/10,d+=2;
}
return o
}
var out = "";
for (var i = 0; i < 100; i++) {
out += toRoman(i) + "\n";
}
document.getElementById("output").innerHTML = out;
<pre id="output"></pre>
function convertToRoman(num) {
var romans = {
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'
};
var popped, rem, roman = '',
keys = Object.keys(romans);
while (num > 0) {
popped = keys.pop();
m = Math.floor(num / popped);
num = num % popped;
console.log('popped:', popped, ' m:', m, ' num:', num, ' roman:', roman);
while (m-- > 0) {
roman += romans[popped];
}
while (num / popped === 0) {
popped = keys.pop();
delete romans[popped];
}
}
return roman;
}
var result = convertToRoman(3999);
console.log(result);
document.getElementById('roman').innerHTML = 'Roman: ' + result;
p {
color: darkblue;
}
<p>Decimal: 3999</p>
<p id="roman">Roman:</p>
I just made this at freecodecamp. It can easily be expanded.
function convertToRoman(num) {
var roman ="";
var values = [1000,900,500,400,100,90,50,40,10,9,5,4,1];
var literals = ["M","CM","D","CD","C","XC","L","XL","X","IX","V","IV","I"];
for(i=0;i<values.length;i++){
if(num>=values[i]){
if(5<=num && num<=8) num -= 5;
else if(1<=num && num<=3) num -= 1;
else num -= values[i];
roman += literals[i];
i--;
}
}
return roman;
}
Here's a regular expression solution:
function deromanize(roman) {
var r = 0;
// regular expressions to check if valid Roman Number.
if (!/^M*(?:D?C{0,3}|C[MD])(?:L?X{0,3}|X[CL])(?:V?I{0,3}|I[XV])$/.test(roman))
throw new Error('Invalid Roman Numeral.');
roman.replace(/[MDLV]|C[MD]?|X[CL]?|I[XV]?/g, function(i) {
r += {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}[i];
});
return r;
}
I really liked the solution by jaggedsoft but I couldn't reply because my rep is TOO LOW :( :(
I broke it down to explain it a little bit for those that don't understand it. Hopefully it helps someone.
function convertToRoman(num) {
var lookup =
{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},roman = '',i;
for ( i in lookup ) {
while ( num >= lookup[i] ) { //while input is BIGGGER than lookup #..1000, 900, 500, etc.
roman += i; //roman is set to whatever i is (M, CM, D, CD...)
num -= lookup[i]; //takes away the first num it hits that is less than the input
//in this case, it found X:10, added X to roman, then took away 10 from input
//input lowered to 26, X added to roman, repeats and chips away at input number
//repeats until num gets down to 0. This triggers 'while' loop to stop.
}
}
return roman;
}
console.log(convertToRoman(36));
IF this number in HTMLElement (such as span), we recommend to Add HTML attribute data-format :
Number.prototype.toRoman = function() {
var e = Math.floor(this),
t, n = "",
i = 3999,
s = 0;
v = [1e3, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1], r = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
if (e < 1 || e > i) return "";
while (s < 13) {
t = v[s];
while (e >= t) {
e -= t;
n += r[s]
}
if (e == 0) return n;
++s
}
return ""
};
var fnrom = function(e) {
if (parseInt(e.innerHTML)) {
e.innerHTML = parseInt(e.innerHTML).toRoman()
}
};
setTimeout(function() {
[].forEach.call(document.querySelectorAll("[data-format=roman]"), fnrom)
}, 10)
Phase <span data-format="roman">4</span> Sales
function convertToRoman(num) {
let roman = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"];
let arabic = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
let index = 0;
let result = "";
while (num > 0) {
if (num >= arabic[index]) {
result += roman[index];
num -= arabic[index];
} else index++;
}
return result;
}
/*my beginner-nooby solution for numbers 1-999 :)*/
function convert(num) {
var RomNumDig = [['','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']];
var lastDig = num%10;
var ourNumb1 = RomNumDig[0][lastDig]||'';
if(num>=10) {
var decNum = (num - lastDig)/10;
if(decNum>9)decNum%=10;
var ourNumb2 = RomNumDig[1][decNum-1]||'';}
if(num>=100) {
var hundNum = ((num-num%100)/100);
var ourNumb3 = RomNumDig[2][hundNum-1]||'';}
return ourNumb3+ourNumb2+ourNumb1;
}
console.log(convert(950));//CML
/*2nd my beginner-nooby solution for numbers 1-10, but it can be easy transformed for larger numbers :)*/
function convert(num) {
var ourNumb = '';
var romNumDig = ['I','IV','V','IX','X'];
var decNum = [1,4,5,9,10];
for (var i=decNum.length-1; i>0; i--) {
while(num>=decNum[i]) {
ourNumb += romNumDig[i];
num -= decNum[i];
}
}
return ourNumb;
}
console.log(convert(9));//IX
function toRoman(n) {
var decimals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];
var roman = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];
for (var i = 0; i < decimals.length; i++) {
if(n < 1)
return "";
if(n >= decimals[i]) {
return roman[i] + toRoman(n - decimals[i]);
}
}
}
This works for all numbers only in need of roman numerals M and below.
function convert(num) {
var code = [
[1000, "M"], [900, "CM"], [800, "DCCC"], [700, "DCC"], [600, "DC"],
[500, "D"], [400, "CD"], [300, "CCC"], [200, "CC"],
[100, "C"], [90, "XC"], [80, "LXXX"], [70, "LXX"], [60, "LX"],
[50, "L"], [40, "XL"], [30, "XXX"], [20, "XX"],
[10, "X"], [9, "IX"], [8, "VIII"], [7, "VII"], [6, "VI"],
[5, "V"], [4, "IV"], [3, "III"], [2, "II"], [1, "I"],
];
var rom = "";
for(var i=0; i<code.length; i++) {
while(num >= code[i][0]) {
rom += code[i][1];
num -= code[i][0];
}
}
return rom;
}
This is the first time I really got stuck on freecodecamp. I perused through some solutions here and was amazed at how different they all were. Here is what ended up working for me.
function convertToRoman(num) {
var roman = "";
var lookupObj = {
1000:"M",
900:"CM",
500:"D",
400:"CD",
100:"C",
90:"XC",
50:"L",
40:"XL",
10:"X",
9:"IX",
4:"IV",
5:"V",
1:"I",
};
var arrayLen = Object.keys(lookupObj).length;
while(num>0){
for (i=arrayLen-1 ; i>=0 ; i--){
if(num >= Object.keys(lookupObj)[i]){
roman = roman + lookupObj[Object.keys(lookupObj)[i]];
num = num - Object.keys(lookupObj)[i];
break;
}
}
}
return roman;
}
convertToRoman(1231);
function convertToRoman(num) {
var romNumerals = [["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 runningTotal = 0;
var roman = "";
for (var i = 0; i < romNumerals.length; i++) {
while (runningTotal + romNumerals[i][1] <= num) {
runningTotal += romNumerals[i][1];
roman += romNumerals[i][0];
}
}
return roman;
}

Categories

Resources