Convert roman number to arabic using javascript - Stack Overflow

Hi I am trying to convert roman numerals to arabic using javascript. I wrote a code but it is failing.

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
 |  Show 4 more ments

7 Answers 7

Reset to default 5

To 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

相关推荐

  • Convert roman number to arabic using javascript - Stack Overflow

    Hi I am trying to convert roman numerals to arabic using javascript. I wrote a code but it is failing.

    1天前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信