Hi I am trying to convert roman numerals to arabic using javascript. I wrote a code but it is failing. The rules I am trying to follow are :
if Larger number is before smaller number then addition and if smaller number is before larger number then subtraction.
Along with that I have few other rules as well like 'D','L' and 'V' can't be repeated at all and 'M' can be repeated only twice (Not sure how to implement this, can I use regex for it and how?)
Code :
function romanToArabic(roman){
if(roman == null)
return -1;
var value;
for(var i=0;i<roman.length;i++){
current = char_to_int(roman.charAt(i));
next = char_to_int(roman.charAt(i+1));
console.log("Current",current);
console.log("Next",next);
if(current >= next){
value = current + next;
console.log(value);
}
else {
console.log(value);
value = next - current;
}
}
return value;
}
function char_to_int(character) {
switch(character){
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return -1;
}
}
console.log(romanToArabic('IIX'));
Hi I am trying to convert roman numerals to arabic using javascript. I wrote a code but it is failing. The rules I am trying to follow are :
if Larger number is before smaller number then addition and if smaller number is before larger number then subtraction.
Along with that I have few other rules as well like 'D','L' and 'V' can't be repeated at all and 'M' can be repeated only twice (Not sure how to implement this, can I use regex for it and how?)
Code :
function romanToArabic(roman){
if(roman == null)
return -1;
var value;
for(var i=0;i<roman.length;i++){
current = char_to_int(roman.charAt(i));
next = char_to_int(roman.charAt(i+1));
console.log("Current",current);
console.log("Next",next);
if(current >= next){
value = current + next;
console.log(value);
}
else {
console.log(value);
value = next - current;
}
}
return value;
}
function char_to_int(character) {
switch(character){
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return -1;
}
}
console.log(romanToArabic('IIX'));
Can somebody help? Would appreciate it!
Added screenshots :
Share Improve this question edited Feb 23, 2018 at 11:37 L Y E S - C H I O U K H 5,0909 gold badges42 silver badges59 bronze badges asked Feb 23, 2018 at 10:49 AnjuAnju 251 gold badge2 silver badges8 bronze badges 9- "I have few other rules as well like 'I' and 'C' can't be repeated at all": ehh, then how do you get 2? – trincot Commented Feb 23, 2018 at 10:53
- Every iteration takes into account two characters, but then the index is incremented by only 1 – GalAbra Commented Feb 23, 2018 at 10:54
- if index is incremented by 2 it would get stuck into endless loop. I have already tried that! the problem is for second iteration it should go to else part but it is still checking the if part – Anju Commented Feb 23, 2018 at 10:58
- @Anju Check my response! It's work fine for me, tested on jsfiddle ! – L Y E S - C H I O U K H Commented Feb 23, 2018 at 10:59
- for my case it is giving me wrong output plus in the validator it reads for the value which starts with M – Anju Commented Feb 23, 2018 at 11:05
7 Answers
Reset to default 5To those, who might need to translate conventional roman numbers as opposed to irregular subtractive notation (e.g. 'IIX' instead of 'VIII' for 8), I might suggest my own, slightly shorter method:
const test = ['XIV'/*14*/, 'MXMVI'/*1996*/, 'CII'/*102*/, 'CDI'/*401*/];
const roman2arabic = s => {
const map = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000};
return [...s].reduce((r,c,i,s) => map[s[i+1]] > map[c] ? r-map[c] : r+map[c], 0);
};
console.log(test.map(roman2arabic));
.as-console-wrapper {min-height: 100%}
Though, it can be modified to follow unconventional logic:
const test = ['IIV'/*3*/,'XXMMII'/*1982*/, 'IIIXV'/*12*/, 'XII'/*conventional 12*/];
const roman2arabic = s => {
const map = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000};
return [...s]
.reduceRight(({sum,order},c,i,s) =>
Object.keys(map).indexOf(c) < order ?
{sum: sum-map[c], order} :
{sum: sum+map[c], order: Object.keys(map).indexOf(c)},
{sum:0,order:Object.keys(map).indexOf(s[s.length-1])})
.sum;
};
console.log(test.map(roman2arabic));
.as-console-wrapper {min-height: 100%}
The issue is that your code only subtracts the value corresponding to one character, while in IIX
you need to subtract twice (although that kind of representation for the number 8 is quite unconventional -- 8 would normally be represented as VIII
).
The solution is to keep collecting a separate sum for when the symbol is the same, so that after reading the first two "I", you have two separate sums:
- total: 2
- value of all "I": 2
Then when you encounter the "X" and detect that a subtraction is needed, you first undo the addition already done for the grand total, and then perform the subtraction with the value you collected for the "I":
- total: -2
After this, you start with a reset value for "X":
- total: 10 + -2 = 8
- value for all "X": 10
Here is your code adapted for that to happen:
function romanToArabic(roman){
if(roman == null)
return -1;
var totalValue = 0,
value = 0, // Initialise!
prev = 0;
for(var i=0;i<roman.length;i++){
var current = char_to_int(roman.charAt(i));
if (current > prev) {
// Undo the addition that was done, turn it into subtraction
totalValue -= 2 * value;
}
if (current !== prev) { // Different symbol?
value = 0; // reset the sum for the new symbol
}
value += current; // keep adding same symbols
totalValue += current;
prev = current;
}
return totalValue;
}
function char_to_int(character) {
switch(character){
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return -1;
}
}
console.log(romanToArabic('IIX'));
As for your additional question to limit the number of consecutive "I" to at most two, "D" at most one, ... you could use a regular expression test at the start of your function:
if (/III|XXX|CCC|MMM|VV|LL|DD|[^IVXLCDM]/.test(roman))
return -1;
You can just append other invalid sub-sequences separated by |
. For instance, if you would not want an "I" to appear directly in front of "L", "C", "D" or "M", then extend to:
if (/III|XXX|CCC|MMM|VV|LL|DD|[^IVXLCDM]|I[LCDM]/.test(roman))
return -1;
const romans = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 };
// MXMIV
function roman2arabic(nums){
let sum = 0;
const numsArr = nums.split('');
const isSimpleRoman = (num) => num in romans;
const arabicCompare = (current, prev) => romans[current] < romans[prev];
const orderNums = (acc, current) => {
const prev = acc[acc.length - 1] || null;
const arabCurrent = romans[current];
if (prev && isSimpleRoman(prev) && arabicCompare(current, prev)) {
sum -= arabCurrent;
acc.pop() && acc.push(current + prev);
} else {
sum += arabCurrent;
acc.push(current);
}
return acc;
};
return numsArr.reduceRight(orderNums, []) && sum;
}
const romans = { 'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000 };
function roman2arabicRecursion(nums){
const numsArr = nums.split('');
const recursion = (arr, index, sum) => {
const current = arr[index];
const prev = arr[index + 1] || null;
if(prev && romans[current] < romans[prev]){
sum -= romans[current];
} else {
sum += romans[current];
}
if(index === 0) return sum;
return recursion(arr, index - 1, sum);
}
return recursion(numsArr, numsArr.length - 1, 0);
};
Alternatively || operator could be used isntead of ??
const toArabic = (romanNumber) => {
const map = {
M: 1000,
D: 500,
C: 100,
L: 50,
X: 10,
V: 5,
I: 1,
};
const nums = romanNumber.split('');
let result = 0;
for (let i = 0; i < nums.length; i += 1) {
const first = map[nums[i]];
const second = map[nums[i + 1]] ?? 0;
if (first < second) {
result += second - first;
i += 1;
} else {
result += first;
}
}
return result;
};
console.log(toArabic('CMXI')); // 911
console.log(toArabic('MXXIV')); // 1024
We are so used to reading from Left to Right, we overlook the Right to Left alternatives.
The whole point of Roman notation is you want to check if V bees before X
That is much easier when you reverse the Roman string.
Or in JavaScript, use the hardly ever used reduceRight
method
(code optimized for better GZIP/Brotli pression)
const romanToArabic = (input) => [...input].reduceRight((
acc,
letter,
idx,
arr,
value = {m:1000, d:500, c:100, l:50, x:10, v:5, i:1}[letter.toLowerCase()],
doubleSubtraction = letter == arr[idx + 1] // ignore IIX notation
) => {
if (value < acc.high && !doubleSubtraction)
acc.Arabic -= value;
else
acc.Arabic += acc.high = value;
//console.log(idx, letter, acc, 'value:', value, acc.high, arr[idx + 1]);
return acc;
}, { high:0, Arabic:0 }).Arabic; // return Arabic value
//TESTS
Object.entries({
"cxxiv": 124,
"ix": 9,
"iix": 10,
"xL": 40,
"MMMDXLIX": 3549,
"MMMMCMXCIX": 4999}
).map(([roman,value])=>{
let converted = romanToArabic(roman);
console.log(roman, "=", converted);
console.assert(converted == value, "wrong conversion,", roman, "must be", value)
})
I resolved this exercise like this:
function arabic(num) {
let ch;
let sum = 0;
for (let i = 0; i < num.length; i++) {
ch = num[i];
switch (ch) {
case 'I':
if (num[i + 1] === 'V' || num[i + 1] === 'X') {
continue;
}
sum = sum + 1;
break;
case 'V':
if (num[i - 1] === 'I') {
sum = sum + 4;
break;
}
sum = sum + 5;
break;
case 'X':
if (num[i - 1] === 'I') {
sum = sum + 9;
break;
}
if (num[i + 1] === 'C') {
continue;
}
sum = sum + 10;
break;
case 'L':
sum = sum + 50;
break;
case 'C':
if (num[i + 1] === 'D' || num[i + 1] === 'M') {
continue;
}
if (num[i - 1] === 'X') {
sum = sum + 90;
break;
}
sum = sum + 100;
break;
case 'D':
if (num[i - 1] === 'C') {
sum = sum + 400;
break;
}
sum = sum + 500;
break;
case 'M':
if (num[i - 1] === 'C') {
sum = sum + 900;
break;
}
sum = sum + 1000;
break;
}
}
return sum;
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742281256a4414528.html
评论列表(0条)