freeCodeCamp Algorithm Challenge Guide: Roman Numeral Converter

freeCodeCamp Algorithm Challenge Guide: Roman Numeral Converter
0

#1

:triangular_flag_on_post: Remember to use Read-Search-Ask if you get stuck. Try to pair program :busts_in_silhouette: and write your own code :pencil:

:checkered_flag: Problem Explanation:

You will create a program that converts an integer to a Roman Numeral.

Relevant Links

:speech_balloon: Hint: 1

Creating two arrays, one with the Roman Numerals and one with the decimal equivalent for the new forms will be very helpful.

try to solve the problem now

:speech_balloon: Hint: 2

If you add the numbers to the arrays that go before the new letter is introduced, like values for 4, 9, and 40, it will save you plenty of code.

try to solve the problem now

:speech_balloon: Hint: 3

You can’t have more than three consecutive Roman numerals together.

try to solve the problem now

Spoiler Alert!

687474703a2f2f7777772e796f75726472756d2e636f6d2f796f75726472756d2f696d616765732f323030372f31302f31302f7265645f7761726e696e675f7369676e5f322e676966.gif

Solution ahead!

:beginner: Basic Code Solution:

var convertToRoman = function(num) {

  var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
  var romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];

  var romanized = '';

  for (var index = 0; index < decimalValue.length; index++) {
    while (decimalValue[index] <= num) {
      romanized += romanNumeral[index];
      num -= decimalValue[index];
    }
  }

  return romanized;
}

// test here
convertToRoman(36);

:rocket: Run Code

Code Explanation:

  • We start off by creating two arrays with default conversion with matching indices. These are called decimalValue and romanNumeral. We also create an empty string variable, romanized, which will house the final roman number.
  • Using a for loop, we loop through the indicies of the decimalValue array. We continue to loop until while the value at the current index will fit into num.
  • Next, we add the roman numeral and decrease num by the decimal equivalent.
  • Finally, we return the value of romanized.

Relevant Links

:sunflower: Intermediate Code Solution:

function convertToRoman(num) {
 var romans = ["I", "V", "X", "L", "C", "D", "M"],
     ints = [],
     romanNumber = [],
     numeral = "";
  while (num) {
    ints.push(num % 10);
    num = Math.floor(num/10);
  }
  for (i=0; i<ints.length; i++){
      units(ints[i]);
  }
  function units(){
    numeral = romans[i*2];
    switch(ints[i]) {
      case 1:
        romanNumber.push(numeral);
        break;
      case 2:
        romanNumber.push(numeral.concat(numeral));
        break;
      case 3:
        romanNumber.push(numeral.concat(numeral).concat(numeral));
        break;
      case 4:
        romanNumber.push(numeral.concat(romans[(i*2)+1]));
        break;
      case 5:
        romanNumber.push(romans[(i*2)+1]);
        break;
      case 6:
        romanNumber.push(romans[(i*2)+1].concat(numeral));
        break;
      case 7:
        romanNumber.push(romans[(i*2)+1].concat(numeral).concat(numeral));
        break;
      case 8:
        romanNumber.push(romans[(i*2)+1].concat(numeral).concat(numeral).concat(numeral));
        break;
      case 9:
        romanNumber.push(romans[i*2].concat(romans[(i*2)+2]));
      }
    }
  return romanNumber.reverse().join("").toString();
}

// test here
convertToRoman(97);

:rocket: Run Code

Code Explanation:

  • Create an array of Roman Numerals (romans).
  • Use a for loop to create an array of the digits (ints) in the number.
  • Loop through the array of digits (base 10) and as you do, increment the Roman Numeral (base 5) index by 2 (numeral = romans[i*2]).
  • Within the loop, use Switch Case to push the proper Roman Numerals (backwards) onto that array.
  • Reverse the Roman Numerals array and turn it into a string.

Relevant Links

:sunflower: Intermediate Code Solution:

function convertToRoman(num) {
  var romans = [
  // 10^i 10^i*5
    ["I", "V"], // 10^0
    ["X", "L"], // 10^1
    ["C", "D"], // 10^2
    ["M"]       // 10^3
  ],
      digits = num.toString()
        .split('')
        .reverse()
        .map(function(item, index) {
          return parseInt(item);
        }),
      numeral = "";
  
  // Loop through each digit, starting with the ones place
  for (var i=0; i<digits.length; i++) {
    // Make a Roman numeral that ignores 5-multiples and shortening rules
    numeral = romans[i][0].repeat(digits[i]) + numeral;
    // Check for a Roman numeral 5-multiple version
    if (romans[i][1]) {
      numeral = numeral
        // Change occurrences of 5 * 10^i to the corresponding 5-multiple Roman numeral
        .replace(romans[i][0].repeat(5), romans[i][1])
        // Shorten occurrences of 9 * 10^i
        .replace(romans[i][1] + romans[i][0].repeat(4), romans[i][0] + romans[i+1][0])
        // Shorten occurrences of 4 * 10^i
        .replace(romans[i][0].repeat(4), romans[i][0] + romans[i][1]);
    }
  }
  
  return numeral;
}

// test here
convertToRoman(36);

:rocket: Run Code

Code Explanation:

  • Use an array (romans) to create a matrix containing the Roman numeral for a given power of 10 and, if available, the Roman numeral for that power of 10 times 5.
  • Convert the input number (num) to a reversed array of digits (digits) so that we can loop through those digits starting with the ones position and going up.
  • Loop through each digit, starting with the ones place, and create a Roman numeral string by adding each higher-power Roman numeral to the start of the numeral string a number of times equal to digit. This initial string ignores the Roman numerals that are a power of 10 times 5 and also ignores shortening rules.
  • If the relevant power of 10 has a 5-multiple Roman numeral, in numeral, replace 5-in-a-row occurrences with the relevant 5-multiple Roman numeral (i.e., V, L, or D) and shorten occurrences of 9 * 10^i (e.g., VIIII to VIX) and 4 * 10^i (e.g., XXXX to XL). Order is important here!
  • Finally, return numeral.

Relevant Links

:clipboard: NOTES FOR CONTRIBUTIONS:

  • :warning: DO NOT add solutions that are similar to any existing solutions. If you think it is similar but better, then try to merge (or replace) the existing similar solution.
  • Add an explanation of your solution.
  • Categorize the solution in one of the following categories — Basic, Intermediate and Advanced. :traffic_light:
  • Please add your username only if you have added any relevant main contents. (:warning: DO NOT remove any existing usernames)

See :point_right: Wiki Challenge Solution Template for reference.


Ruman Number Converter
Question about these type of exercices (Roman Numeral Converter & Caesars Cipher)
Would you say this is incredibly inefficient?
How to improve your reasoning skills/your program? [Roman Converter]
Should this be happening? - Algorithm Scripting
As a beginner, how do I know if I'm writing "good" code?
#2

#3

#4
function convertToRoman(num) {
 var romanNumerals = ['M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I'];//an array of roman numerals in order from largest to smallest
 var decimals = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1];//an array of decimals values that match the index of the roman numerals
 var romanized='';//creates an empty string call romanized

 for (var i=0;i<decimals.length;i++){ //this loops through the decimals array
while (decimals[i]<=num){ //do something as soon as the indexed number for the decimals array is less than the input number
  romanized += romanNumerals[i]; //push the roman numeral with the same index value as the decimals to the romanized string
  num -= decimals[i]; //reduce the input number by the matching decimals index number
}
}
return romanized;
}

convertToRoman(36);

I added comments to each part of the code to try to explain what it is doing.


#6

Weird: the basic version seems much more elegant…


#7

Not sure if this is better or worse, but I thought this was a simple, and easier to understand method than what was provided. This may be because it is slightly more hard coded than the other solutions.


function convertToRoman(num) {
num = num.toString();
var str = “” , j = 0, roman = [“I”,“V”,“X”,“L”,“C”,“D”,“M”];
for(i=num.length -1;i>=0;i -= 1){
str = helper(num[i],roman[j],roman[j+1],roman[j+2])+ str;
j += 2;
}
return str;
function helper(num,s,m,b){
var roman = ["",s,s+s,s+s+s,s+m,m,m+s,m+s+s,m+s+s+s,s+b];
return roman[Number(num)];
}
}
convertToRoman(36);

What this is doing is providing the 3 possible characters needed for each digit, for example in the one’s place, the helper functions is called as helper(num,"I","V","X"), and the helper functions then takes the provided number and looks it up in the hard coded array to see what combinations of those 3 characters make the number provided. ie 7 = "VII"
We then loop through each digit in the original number starting with the ones digit until we create the entire sting.


#8

A tricky puzzle becomes easy when you find the conversion tables :slight_smile:

function convertToRoman(num) {
  // adapted from http://rapidtables.com/convert/number/how-number-to-roman-numerals.htm
  
  var map = [
    { d:1, r:'I' },
    { d:4, r:'IV' },
    { d:5, r:'V' },
    { d:9, r:'IX' },
    { d:10, r:'X' },
    { d:40, r:'XL' },
    { d:50, r:'L' },
    { d:90, r:'XC' },
    { d:100, r:'C' },
    { d:400, r:'CD' },
    { d:500, r:'D' },
    { d:900, r:'CM' },
    { d:1000, r:'M' }
  ];
  
  var roman = '';
  
  while (num > 0) {

    // highest mapped decimal less than or equal num
    var max = map[0];
    map.forEach(function(el) {
      if (el.d <= num) {
        max = el;
      }
    });
  
    roman += max.r;
    num -= max.d;

  }
  
  return roman;
}

convertToRoman(36);

#9

This is my solution. Just for the case studying.

function convertToRoman(num) {
  
  var rLits = {
    1:    'I', // * 5
    5:    'V', // * 2
    10:   'X', // * 5
    50:   'L', // * 2
    100:  'C', // * 5
    500:  'D', // * 2
    1000: 'M'  // * 5
  };
  
  num = (num + '').split('');
    
  var str = '';
  
  for (var i = num.length - 1, bit = 1; i >= 0; i--, bit*=10) {   
    var digit = parseInt(num[i]);
    var basePrev = '';
    var baseCurr = rLits[5*bit];
    
    if (digit > 5) {
      basePrev = rLits[5*bit];
      baseCurr = rLits[10*bit];
      digit = digit % 5;
    } else if (digit === 5) {
      str = rLits[5*bit] + str;
    }
    
    switch (digit) {
      case 1: str = basePrev + rLits[1*bit] + str; 
        break;
      case 2: str = basePrev + rLits[1*bit].repeat(2) + str; 
        break;
      case 3: str = basePrev + rLits[1*bit].repeat(3) + str;
        break;
      case 4: str = rLits[1*bit] + baseCurr + str;
    }
  }
  
  return str;
}

#10

Here is the modification of basic solution that uses recursion. Not sure how I can do it better.

/* jshint esnext: true */

var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ];
var romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];
var repeat = (str, count) => new Array(count).fill(str).join('');

function _convertToRoman(num, decimals, romans) {
  if (num === 0) {
    return '';
  }
  var div = Math.floor(num / decimals[0]);
  var mod = num % decimals[0];
  return (repeat(romans[0], div)) + _convertToRoman(mod, decimals.slice(1), romans.slice(1));
}

function convertToRoman(num) {
  return _convertToRoman(num, decimalValue, romanNumeral);
}

convertToRoman(36);

#11

My pseudo-solution,


function convertToRoman(num) {
  
  // 1st: Splice num into unit, deci, cent, 
  var listinv = num.toString(10).split("").map(Number);
  // 2nd: Reverse the numbers,
  var list = listinv.reverse();
  // 3rd: get the number position
  var unit = list[0];
  var deci = list [1];
  var cent = list [2];
  var mill = list [3];
  
  // 4th extraodinary cases
  switch (num) {
    case 10: num = "X"; return num; break;
    case 100: num = "C"; return num; break;
    case 500: num = "D"; return num;  break;
    case 1000: num = "M"; return num; break;
  }
  // 5th: case 1: one unit (1-9) 
    
  switch (unit) {
    case 1: unit = "I";    break;
    case 2: unit = "II";   break; 
    case 3: unit = "III";  break;
    case 4: unit = "IV";   break;
    case 5: unit = "V";    break;
    case 6: unit = "VI";   break;
    case 7: unit = "VII";  break;
    case 8: unit = "VIII"; break;
    case 9: unit = "IX";      
}
   
  // if one 
  if (list.length ===1) {
    return unit;
  }
  // 6th:  Case 2: Two units (10 - 19);
    switch (deci) {
      case 1: deci = "X"; break; 
      case 2: deci = "XX"; break;
      case 3: deci = "XXX"; break;
      case 4: deci = "XL"; break;
      case 5: deci = "L"; break;
      case 6: deci = "LX"; break;
      case 7: deci = "LXX"; break;
      case 8: deci = "LXXX"; break;
      case 9: deci = "XC";  break;
      case 0: deci = ""; break;
    }
if (list.length ===2) {
    return deci.concat(unit);
}
    //7th: case 101 - 999

    switch (cent) {
      case 1: cent = "C"; break;
      case 2: cent = "CC"; break;
      case 3: cent = "CCC"; break;
      case 4: cent = "CD"; break;
      case 5: cent = "D"; break;
      case 6: cent = "DC"; break;
      case 7: cent = "DCC"; break;
      case 8: cent = "DCCC"; break;
      case 9: cent = "CM"; break;
      case 0: cent = ""; 
    }
    if (list.length === 3) {
    return cent.concat(deci,unit);
  }
  // 8th: case 1001- 399
  
  switch (mill) {
    case 1: mill = "M"; break;
    case 2: mill = "MM"; break;
    case 3: mill = "MMM"; 
  }

  if (list.length === 4) {
    return mill.concat(cent,deci,unit);
    
  }
 return list;
}

convertToRoman(1004);  


#12

I noticed a pattern in the roman numerals using 3 chars in the same order depending on the length of the number (1000, 100 or 10) , so used that to create a separate fn that uses parameters to set & output the repeating pattern.

function numerify (str, n, op) {
  let o, m, t;
  switch(op) {
    case 1000 : o = 'C', m = 'D', t = 'M'; break;
    case 100  : o = 'X', m = 'L', t = 'C'; break;
    case 10   : o = 'I', m = 'V', t = 'X'; break;
  }
  if (n >= 4) {
    if (n == 4) {
      return str+= o + m;
    } else if (n == 9) {
      return str+= o + t;
    } else {
      if (n - 5 == 0) {
        return str+= m;
      } else {
        str+= m;
        for (let i = 0 ; i < n-5 ; i++) {
          str+= o;
        }
        return str;
      }
    }
  } else {
    for (let i = 0 ; i < n ; i++) {
      str+= o;
    }
    return str;
  }
}

function convertToRoman(num) {
  let str = '';
  num = num.toString().split('');

  switch (num.length) {
    case 4:
      for (let i = 0 ; i < num[0] ; i++) {
        str+= 'M';
      };
      num = num.slice(1);
    case 3:
      str = numerify(str, num[0], 1000);
      num = num.slice(1);
    case 2:
      str = numerify(str, num[0], 100);
      num = num.slice(1);
    case 1:
      str = numerify(str, num[0], 10);
      return str;
  }
}

#13

I stringified the argument(num) to array.And unshift the array with “mark” elements(if num is less than 4 digits because the max digits of the course is 3999:P) and reverse it.
With all methods done I can match the index of array with the roman numeric easily using a increase of (index*2).

function convert(num) {
     var roNum=[];
     var newNum=[];
     num=num+"";
     num=num.split("");
      
     var rule=["I","V","X","L","C","D","M","V_","X_","L_"];
      newNum=num.slice();
     while(newNum.length<4){
       newNum.unshift("mark");
     }
      newNum.reverse();
      
     for(i=0;i<4;i++){
       if(newNum[i]==="mark"){
         break;
       }
       else{
         switch(newNum[i]){
           case "0":
             break;
           case "1":roNum.unshift(rule[0+2*i]);
             break;
           case "2":roNum.unshift(rule[0+2*i]+rule[0+2*i]);
             break;
           case "3":roNum.unshift(rule[0+2*i]+rule[0+2*i]+rule[0+2*i]);
             break;
           case "4":roNum.unshift(rule[0+2*i]+rule[1+2*i]);
             break;
           case "5":roNum.unshift(rule[1+2*i]);
             break;
           case "6":roNum.unshift(rule[1+2*i]+rule[0+2*i]);
             break;  
           case "7":roNum.unshift(rule[1+2*i]+rule[0+2*i]+rule[0+2*i]);
             break; 
           case "8":roNum.unshift(rule[1+2*i]+rule[0+2*i]+rule[0+2*i]+rule[0+2*i]);
             break; 
           case "9":roNum.unshift(rule[0+2*i]+rule[2+2*i]);
             break; 
          }
         
       }
     }
      return roNum.join("");
    }

    convert(3999);

#14

Please give me some feedback on my original solution. The Basic Code Solution is very cool, and elegant. I think mine is pretty straightforward so I kind of like it too.

Let me know what you think. Criticize it!

Edit: Thinking about this I realized this is part of the Algorithm Challenge but the only thing that could be considered an algorithm is the switch statement?

function convertToRoman(num) {
    num = num.toString();
    
    var ones = ["", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"];
    var tens = ["", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"];
    var hundreds = ["", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"];
    var thousands = "M";

    // Creates a var for each decimal place
    var x = num[num.length - 1];
    var xx = num[num.length - 2];
    var xxx = num[num.length - 3];
    var xxxx = num.slice(0, -3);

    // Creates the roman for each decimal place
    var num1 = ones[x];
    var num2 = tens[xx];
    var num3 = hundreds[xxx];
    var num4 = thousands.repeat(Number(xxxx));

    switch (num.length) {
        case 1:
            num = num1;
            break;
        case 2:
            num = num2 + num1;
            break;
        case 3:
            num = num3 + num2 + num1;
            break;
        default:
            num = num4 + num3 + num2 + num1;
            break;
    }
    return num;
}

convertToRoman(494);

Solution
Edit: Edited the link, strange it went to a fork of my original link. I think due to changes in the repl.it website.


#15

Basic inelegant solution.

function convertToRoman(num) {
  var x = num+"";
  var y = x.split("");
  var j=0;
  var z=y.length;    
  var a=[["I","V","X"],["X","L","C"],["C","D","M"],["M","V","X"]];
  var b=[];
  
  for(i=y.length-1;i>=0;i-=1){
    y[j]+=("0".repeat(i));
    j+=1;
  }
  for(k=0;k<z;k+=1){
    y[k]=parseInt(y[k]);
  }
  y.reverse();
  
  for(l=0;l<y.length;l+=1){
    if(y[l]===0){
      b.push("");
    } else if(y[l]<parseInt(4+"0".repeat(l))){
      b.push(a[l][0].repeat(parseInt(y[l].toString().slice(0,1))));
    } else if(y[l]==parseInt(4+"0".repeat(l))){
      b.push(a[l][0] + a[l][1]);
    } else if(y[l]==parseInt(5+"0".repeat(l))){
      b.push(a[l][1]);
    } else if(y[l]>parseInt(5+"0".repeat(l)) && y[l]<parseInt(9+"0".repeat(l))){
      b.push(a[l][1]+a[l][0].repeat(parseInt(y[l].toString().slice(0,1)-5))); 
    } else {
      b.push(a[l][0]+a[l][2]);
    }
  }
  
  b.reverse();
  b=b.join("");
    
  return b;
}

convertToRoman(501);

#16

Did this with quick 'n dirty => iterative function that calls itself again. Not beautiful, not very efficient, but works. I guess one could find more elegant solution based on lists but this was easier… :slight_smile:

function convertToRoman(num) {
  if (num-1000>=0) return "M" + convertToRoman(num-1000);
  if (num-900>=0) return "CM" + convertToRoman(num-900);
  if (num-500>=0) return "D" + convertToRoman(num-500);
  if (num-400>=0) return "CD" + convertToRoman(num-400);
  if (num-100>=0) return "C" + convertToRoman(num-100);
  if (num-90>=0) return "XC" + convertToRoman(num-90);
  if (num-50>=0) return "L" +convertToRoman(num-50);
  if (num-40>=0) return "XL" +convertToRoman(num-40);
  if (num-10>=0) return "X" + convertToRoman(num-10);
  if (num-9>=0) return "IX" + convertToRoman(num-9);
  if (num-5>=0) return "V" + convertToRoman(num-5);
  if (num-4>=0) return "IV" + convertToRoman(num-4);
  if (num-1>=0) return "I" + convertToRoman(num-1);
return "";
}

#17

Intermediate Solution

Pros:

  1. Short & Simple to understand сompared to other intermediate solutions
  2. It replaces addition (from basic solution on topic) by multiplication, in theory it means less operations for process

Minuses:
… I can’t found any minuses

Code Explanation:
In this code I use “integer division” and “division with remainder”. JS has no native support for integer division, so I used this expression: Math.floor(num1 / num2).
For example:
Math.floor(11 / 10) = 1,
Math.floor(11 / 5) = 2,
Math.floor(11 / 100) = 0.

Source code:

function convertToRoman(num) {
  var decimalValue = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ],
      romanNumeral = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ],
      romanized = '',
      dec, div;

  for (var i = 0; i < decimalValue.length; i++) {
    dec = decimalValue[i];
    div = Math.floor(num / dec);
    
    if(div > 0) {
      romanized += romanNumeral[i].repeat(div);
      num = num % dec;
    }
  }
  
  return romanized;
}

convertToRoman(36);

#18

Here is my code:

function convertToRoman(num) {
 
var 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 };
var roman ='';
for(var i in map){
  
  while(num >= map[i]){
    roman += i;
    num -= map[i];
  }
}

return roman;
}

//test
convertToRoman(501);

#19

After being stuck in an infinite loop trying to get this to work, I finally got it working today!

var numbers = [ 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 ],
    romans = [ 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I' ];


function convertToRoman(input) {
  var returnStr = "";

  numbers.map(function(number, i) {
      while (input >= number) {
        returnStr += romans[i];  
        input -= number;
      }
  });
  
 return returnStr;
}

convertToRoman(36);

#20

I went with a recursive approach. I like this approach because it is relatively simply to implement and understand and scalable (if in theory more Roman Numerals were one day added).

function convertToRoman(num) {
 var convertTable = 
     [
       [1, 'I'],
       [4, 'IV'],
       [5, 'V'],
       [9, 'IX'],
       [10, 'X'],
       [40, 'XL'],
       [50, 'L'],
       [90, 'XC'],
       [100, 'C'],
       [400, 'CD'],
       [500, 'D'],
       [900, 'CM'],
       [1000, 'M']
     ];
  
  convertTable.sort(function(a, b)
  {
    return b[0] - a[0];
  });
  return conversion(num, convertTable); 
}

function conversion(num, arr)
{
  if (num <= 0)
    return '';  
  if (arr[0][0] > num)
    {
      arr.shift();  
      return conversion(num, arr);
    }
  
  return arr[0][1] + conversion(num - arr[0][0], arr) ;  
}

#21

Solved this using template based solution.

var numerals = [“I”,“V”,“X”,“L”,“C”,“D”,“M”];
var templates = {
“1”: [0],
“2”: [0,0],
“3”: [0,0,0],
“4”: [0,1],
“5”: [1],
“6”: [1,0],
“7”: [1,0,0],
“8”: [1,0,0,0],
“9”: [0,2]
};

function convertToRoman(num) {
var str = num.toString();
var roman = “”;

for(var i in str){
var decimalPlace = str.length - i - 1;
var template = templates[str[i]];

for(var j in template){
  roman += numerals[2*decimalPlace + template[j]];
}

}

return roman;
}