Converting a Roman Numeral to an Integer

The post walks through monkey patching the String class to add a to_arabic_number method that converts a Roman numeral to an Integer. See the Wikipedia page on Roman numerals for more information about how to convert a Roman numeral to an Arabic number.

The code for this post is in this GitHub repository.

The iterative solution will leverage a hash that maps the Roman numerals to the respective numbers.

class String  
  private

  def roman_mapping
    {
      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"
    }
  end
end

The integer value of a Roman numeral can be calculated by iterating over the characters in the Roman numeral and keeping a running tally of the total value associated with each character. The “exception” characters, like “IV” require special attention since they should be counted as a single character, not two separate characters.

Here is the code for the iterative to_arabic method:

def to_arabic
  result = 0
  str = self
  roman_mapping.values.each do |roman|
    while str.start_with?(roman)
      result += roman_mapping.invert[roman]
      str = str.slice(roman.length, str.length)
    end
  end
  result
end

Solving to_arabic recursively is a bit cleaner. “MIV”.to_arabic is equivalent to 1,000 + “IV”.to_arabic, which is equivalent to 1,004 + “”.to_arabic. The recursive solution adds the of the first Roman numeral to the result argument and terminates when the string argument is empty.

def to_arabic(str = self, result = 0)
  return result if str.empty?
  roman_mapping.values.each do |roman|
    if str.start_with?(roman)
      result += roman_mapping.invert[roman]
      str = str.slice(roman.length, str.length)
      return to_arabic(str, result)
    end
  end
end

Both the iterative and recursive to_arabic methods should make the following tests pass:

describe String do
  context "#to_arabic" do
    it "converts a Roman numeral to an integer" do
      expect("MMCLIV".to_arabic).to eq 2_154
    end

    it "converts 'IV' to 4" do
      expect("IV".to_arabic).to eq 4
    end

    it "converts 'MMMMCMXCIX' to 4999" do
      expect("MMMMCMXCIX".to_arabic).to eq 4999
    end
  end
end
Advertisements

2 thoughts on “Converting a Roman Numeral to an Integer

  1. Pingback: Roman Numeral Converter with JavaScript and underscore.js | Ruby/Rails Programming

  2. I like your solution, there is another way to do it. See below.
    ROMAN_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,

    def to_arabic(roman)
    re = Regexp.new(ROMAN_MAP.keys.join(‘|’))
    roman.scan(re).inject(0) do |number, key|
    number + ROMAN_MAP[key]
    end
    end

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s