Roman to numeral converter javascript

In this tutorial we will create a roman numeral to integer and integer to roman, two in one converter in javascript.

Roman numerals are a number system that originated in ancient Rome. Numbers in this system are represented by combinations of letters where each letter have fixed integer value.

Roman numeral converter javascript

By building this project you will learn many important things in problem solving.

  • HTML:- Layout the elements.
  • CSS:- Styling the layout.
  • Javascript:- Creating two different programs to convert roman to integer(Easy) and integer to roman(Hard).

I assume you have got a good idea about what are we going to do, so lets start building it.

Creating the layout of the roman to numeral converter.

As you can see in the above image we have two sections, one which has the calculator and second which as the info of the letter and their associated values.

So we will create two sections inside a wrapper and then each section will have their respective elements.

<!-- Wrapper -->
<main class="wrapper">
    <!-- Converter -->
    <section class="calc box">
    </section>

    <!-- Info chart -->
    <section class="info box">
    </section>
</main>

Converter layout

Converter area contains a checkbox which helps to decide which type of conversion we are going to do.

Then an input area which will take the input and output area which will show the result.

A button which will convert the value when clicked.

<!-- Converter -->
<section class="calc box">
    <h1>Convert Roman To Integer</h1>

    <!-- Checkbox -->
    <div class="field-group">
        <input type="checkbox" name="roman" />
        <label for="roman">Numeral To Roman</label>
    </div>

    <!-- Input area -->
    <div class="field-group">
        <input type="text" name="conversion-input" />
    </div>

    <!-- Button -->
    <span class="btn">Convert</span>

    <!-- Output area -->
    <p class="output"></p>
</section>

Info Layout

In this we just add multiple rows which show the letter and its associated value.

<!-- Info chart -->
<section class="info box">
    <div class="rows">
        <span>Roman Letter</span>
        <span>Integer Value</span>
    </div>
    <div class="rows">
        <span>I</span>
        <span>1</span>
    </div>
    <div class="rows">
        <span>V</span>
        <span>5</span>
    </div>
    <div class="rows">
        <span>X</span>
        <span>10</span>
    </div>
    <div class="rows">
        <span>L</span>
        <span>50</span>
    </div>
    <div class="rows">
        <span>C</span>
        <span>100</span>
    </div>
    <div class="rows">
        <span>D</span>
        <span>500</span>
    </div>
    <div class="rows">
        <span>M</span>
        <span>1000</span>
    </div>
</section>

Styling the layout.

I will be using flexbox to align the items. Each section will be flexed depending upon how we want to show them.

* {
  box-sizing: border-box;
}

.wrapper {
  display: flex;
  justify-content: space-between;
  align-items: stretch;
  max-width: 1000px;
  margin: 60px auto;
  flex-wrap: wrap;
}

.calc,
.info {
  display: inline-flex;
  justify-content: center;
  align-items: center;
  flex-wrap: wrap;
  flex: 0 1 48%;
  width: 48%;
  padding: 25px;
}

.box {
  box-shadow: 0 0 3px #000;
}

.field-group,
.output {
  display: inline-flex;
  flex: 1 1 100%;
  align-items: center;
  justify-content: center;
  margin-bottom: 20px;
}

.output {
  border: 1px solid #000;
  border-radius: 5px;
  background: #eee;
  min-height: 100px;
  margin: 30px 0;
  font-size: 2.2em;
}

input[type="text"] {
  width: 100%;
  padding: 10px;
  font-size: 2em;
  border: 1px solid #000;
  margin-bottom: 10px;
}

input[type="checkbox"] {
  width: 20px;
  height: 20px;
}

.btn {
  font-size: 1.8em;
  cursor: pointer;
  border: 1px solid red;
  padding: 10px 15px;
  background-color: #f44336;
  box-shadow: 0 0 3px;
  color: #fff;
  transition: color 0.2s ease;
}

.btn:hover {
  color: #000;
}

.rows {
  display: inline-flex;
  flex: 1 1 100%;
  flex-wrap: wrap;
}

.rows > span {
  flex: 1 1 50%;
  text-align: center;
  font-size: 2em;
}

Converting roman numeral to integer and integer to roman with javascript.

There are two function which we will have to create one for each type of conversion.

Converting roman to integer.

It is not easy to come up with a solution to convert roman numeral to integer but I would recommend that you should practice solving this problem by your own.

This is little simple than the second method in which we have to convert the integer to roman.

Still there are few things which we have to take care of before conversion.

  • If we get the input in different case then convert it to uppercase before proceeding any further.
  • Validate the input to make sure we get the roman numeral in proper format. If it is in incorrect format then show an alert with message.
  • If everything is good then perform the conversion.
//Converts roman numeral to integer
const convertRomanToInteger = () => {
  //Regex to validate roman numberal
  const romanNumeralRegex = new RegExp(
    /^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$/
  );

  let { value: roman } = conversionInput;
  roman = roman.toUpperCase();
  const regexResult = romanNumeralRegex.test(roman);

  if (!regexResult) {
    alert("Please enter a valid roman numeral");
    return false;
  }

  //sequence of roman letters
  let arr = ["I", "V", "X", "L", "C", "D", "M"];

  //value of the respective roman letters
  let values = {
    I: 1,
    V: 5,
    X: 10,
    L: 50,
    C: 100,
    D: 500,
    M: 1000,
  };

  let sum = 0;

  //keep track of the previous index
  let prevIndex = 0;

  for (let i = roman.length - 1; i >= 0; i--) {
    //if the current letter is having greater index than previous letter then add values
    if (arr.indexOf(roman[i]) >= prevIndex) {
      sum = sum + values[roman[i]];
    } else {
      //if the current letter is having lesser index than previous letter then sub values
      sum = sum - values[roman[i]];
    }

    //store the index of the previous roman letters
    prevIndex = arr.indexOf(roman[i]);
  }

  //Add the result to the output area
  outputArea.innerHTML = sum;
};

Converting roman to integer

Writing a program for this conversion is little difficult because you will have to first understand the pattern of how roman numeral works.

There are few things which we need to validate before conversion.

  • Make sure the input we receive are only numbers.
  • The max number for which we will be doing conversion is 5000. Because this is the limit for the current case, We can do more than this but we will have to add extra Latin alphabets.

Our conversion function is divided in two different formats, one which converts the value less than 9 because it follows different pattern and then value which is greater than 9 because it follows another pattern.

I would not go into to the details of how this program works, this is a exercise for you to figure it out because it will really increase your problem solving skills.

//Converts integer to roman numeral
const convertIntegerToRoman = () => {
  //Regex to validate if there are only numbers
  const numberRegex = new RegExp(/^\d+$/);

  let { value: num } = conversionInput;
  const regexResult = numberRegex.test(num);

  if (!regexResult) {
    alert("Please enter a valid integer");
    return false;
  }

  if (Number(num) > 4999) {
    alert("Out of range. Please enter a integer less than 5000.");
    return false;
  }

  //Mapping
  const mapping = {
    1: "I",
    5: "V",
    10: "X",
    50: "L",
    100: "C",
    500: "D",
    1000: "M",
  };

  let count = 1;
  let str = "";
  while (num > 0) {
    let last = parseInt(num % 10);
    last *= count;
    if (last < 10) {
      str += lessThan9(last, mapping);
    } else {
      str = greaterThan9(last, mapping) + str;
    }

    count *= 10;
    num = parseInt(num / 10);
  }
  outputArea.innerHTML = str;
};

//If the integer is less than one
//Generte the roman numeral
const lessThan9 = (num, obj) => {
  if (num === 9) {
    return obj[1] + obj[10];
  } else if (num >= 5 && num < 9) {
    return obj[5] + obj[1].repeat(num % 5);
  } else if (num === 4) {
    return obj[1] + obj[5];
  } else {
    return obj[1].repeat(num);
  }
};

//If integer is greater than 9
//Generate the roman numeral
const greaterThan9 = (num, obj) => {
  if (num >= 10 && num < 50) {
    if (num === 10) {
      return obj[10];
    }

    if (num === 40) {
      return obj[10] + obj[50];
    } else {
      return obj[10].repeat(parseInt(num / 10));
    }
  } else if (num >= 50 && num < 100) {
    if (num === 50) {
      return obj[50];
    }

    if (num === 90) {
      return obj[10] + obj[100];
    } else {
      return obj[50] + obj[10].repeat(parseInt((num - 50) / 10));
    }
  } else if (num >= 100 && num < 500) {
    if (num === 100) {
      return obj[100];
    }

    if (num === 400) {
      return obj[100] + obj[500];
    } else {
      return obj[100].repeat(parseInt(num / 100));
    }
  } else if (num >= 500 && num < 1000) {
    if (num === 500) {
      return obj[500];
    }

    if (num === 900) {
      return obj[100] + obj[1000];
    } else {
      return obj[500] + obj[100].repeat(parseInt(num - 500) / 100);
    }
  } else if (num >= 1000 && num < 5000) {
    if (num === 1000) {
      return obj[1000];
    }

    return obj[1000].repeat(parseInt(num / 1000));
  }
};

Now when the convert button is clicked or enter button is pressed we have to perform the conversion, for which we need to get the inputs and show the output.

So lets get all the elements and store them in variable so that we can use them.

const heading = document.querySelector("h1");
const romanToNumeral = document.querySelector("input[type='checkbox']");
const conversionInput = document.querySelector("input[type='text']");
const outputArea = document.querySelector(".output");
const convertButton = document.querySelector(".btn");

When the checkbox is checked we have to update the text to notify user which type of conversion is going to happen.

romanToNumeral.addEventListener("change", (e) => {
  const { checked } = e.target;
  if (checked) {
    heading.innerHTML = "Convert Integer To Roman";
  } else {
    heading.innerHTML = "Convert Roman To Integer";
  }
});

After this when convert button is clicked or enter button is pressed we should perform the conversion, so it is good idea to create a function which will call the appropriate conversion function based on the checkbox value and we call this function to trigger the conversion.

//Call the appropriate conversion function
const calc = () => {
  const { checked } = romanToNumeral;

  if (checked) {
    convertIntegerToRoman();
  } else {
    convertRomanToInteger();
  }
};

//Calculate on convert button click
convertButton.addEventListener("click", () => {
  calc();
});

//Calculate when enter is pressed.
window.addEventListener("keypress", (e) => {
  if (e.key === "Enter") {
    calc();
  }
});

Kudos 🙌🙌 you have learned something today.