Roman numerals to integers
Asked Answered
M

24

15

In data sometimes the same product will be named with a roman numeral while other times it will be a digit.

Example Samsung Galaxy SII verses Samsung Galaxy S2

How can the II be converted to the value 2?

Manvil answered 15/2, 2013 at 17:37 Comment(7)
Have you written any code that you could post?Arp
Any samples of the product numbers with/without roman numbers?Flabbergast
Nah, I haven't given a shot at the code yet. The products are mobile gadgets. Example - Samsung Galaxy SII - Samsung Galaxy S2.Manvil
Automatically fixing bad data is a Bad Idea, don't do it. Just add a feature to allow your client to do the merge easily. Now it is his problem, not yours.Sendal
I agree, just add some aliasing feature that allows you to map “SII” and “S2” to the same product. This would also allow you to add for example code names which HTC commonly uses for all their products.Grannias
Yeah, just add the mapping, so that the different names are recognized as the same thing. Likely there will be other cases where an object is referred to with different names, when a vendor changes the package, etc.Cati
See gist.github.com/wollmich/fb85d529b195a8008e940e488cb739beQuaker
C
28

I've noticed some really complicated solutions here but this is a really simple problem. I made a solution that avoided the need to hard code the "exceptions" (IV, IX, XL, etc). I used a for loop to look ahead at the next character in the Roman numeral string to see if the number associated with the numeral should be subtracted or added to the total. For simplicity's sake I'm assuming all input is valid.

private static Dictionary<char, int> RomanMap = new Dictionary<char, int>()
    {
        {'I', 1},
        {'V', 5},
        {'X', 10},
        {'L', 50},
        {'C', 100},
        {'D', 500},
        {'M', 1000}
    };

public static int RomanToInteger(string roman)
{
    int number = 0;
    for (int i = 0; i < roman.Length; i++)
    {
        if (i + 1 < roman.Length && RomanMap[roman[i]] < RomanMap[roman[i + 1]])
        {
            number -= RomanMap[roman[i]];
        }
        else
        {
            number += RomanMap[roman[i]];
        }
    }
    return number;
}

I initially tried using a foreach on the string which I think was a slightly more readable solution but I ended up adding every single number and subtracting it twice later if it turned out to be one of the exceptions, which I didn't like. I'll post it here anyway for posterity.

public static int RomanToInteger(string roman)
{
    int number = 0;
    char previousChar = roman[0];
    foreach(char currentChar in roman)
    {
        number += RomanMap[currentChar];
        if(RomanMap[previousChar] < RomanMap[currentChar])
        {
            number -= RomanMap[previousChar] * 2;
        }
        previousChar = currentChar;
    }
    return number;
}
Confiteor answered 31/10, 2014 at 5:5 Comment(4)
-1 Because this won't work with IIL - it should output 48, but instead it outputs 50. You should also check the numeral 2 spaces ahead.Contreras
It doesn't work because IIL isn't a valid Roman numeral. Use XLVIII instead.Confiteor
Doesn't work with lower case, though that's easily fixed by adding the extra cases to the dictionary.Careen
A simpler solution would be to add roman = roman.ToUpper(); to the first line of RomanToInteger. You can see it in action here: ideone.com/mq3Sq9Confiteor
S
11

This is my solution

public int SimplerConverter(string number)
    {
        number = number.ToUpper();
        var result = 0;

        foreach (var letter in number)
        {
            result += ConvertLetterToNumber(letter);
        }

        if (number.Contains("IV")|| number.Contains("IX"))
            result -= 2;

        if (number.Contains("XL")|| number.Contains("XC"))
            result -= 20;

        if (number.Contains("CD")|| number.Contains("CM"))
            result -= 200;


        return result;



    }

    private int ConvertLetterToNumber(char letter)
    {
        switch (letter)
        {
            case 'M':
            {
                return 1000;
            }

            case 'D':
            {
                return 500;
            }

            case 'C':
            {
                return 100;
            }

            case 'L':
            {
                return 50;
            }

            case 'X':
            {
                return 10;
            }

            case 'V':
            {
                return 5;
            }

            case 'I':
            {
                return 1;
            }

            default:
            {
                throw new ArgumentException("Ivalid charakter");
            }



        }

    }
Stacked answered 12/7, 2017 at 17:42 Comment(2)
For me, this is by far the best solution. It's super simple and easy to follow. The other solutions are overly complicated. I added this as a string extension and now I can just call romanNumeral.TryParseRoman() to get the int value.Cameo
This. Tried Florian's (see comment) and Wisher's (couldn't get it working in vb.net). This one accurate and fast (less than a millisecond for 1635762 - convert that here: browserling.com/tools/decimal-to-roman)Grajeda
B
6

A more simple and readable C# implementation that:

  • maps I to 1, V to 5, X to 10, L to 50, C to 100, D to 500, M to 1000.
  • uses one single foreach loop (foreach used on purpose, with previous value hold).
  • adds the mapped number to the total.
  • subtracts twice the number added before, if I before V or X, X before L or C, C before D or M (not all chars are allowed here!).
  • returns 0 (not used in Roman numerals) on empty string, wrong letter or not allowed char used for subtraction.
  • remark: it's still not totally complete, we didn't check all possible conditions for a valid input string!

Code:

private static Dictionary<char, int> _romanMap = new Dictionary<char, int>
{
   {'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}
};

public static int ConvertRomanToNumber(string text)
{
    int totalValue = 0, prevValue = 0;
    foreach (var c in text)
    {
        if (!_romanMap.ContainsKey(c))
            return 0;
        var crtValue = _romanMap[c];
        totalValue += crtValue;
        if (prevValue != 0 && prevValue < crtValue)
        {
            if (prevValue == 1 && (crtValue == 5 || crtValue == 10)
                || prevValue == 10 && (crtValue == 50 || crtValue == 100)
                || prevValue == 100 && (crtValue == 500 || crtValue == 1000))
                totalValue -= 2 * prevValue;
            else
                return 0;
        }
        prevValue = crtValue;
    }
    return totalValue;
}
Baccy answered 16/1, 2015 at 3:43 Comment(1)
It will not catch the following case: IXI. Can be solved by adding one more condition and marking found symbols.Unhappy
B
4

I landed here searching for a small implementation of a Roman Numerals parser but wasn't satisfied by the provided answers in terms of size and elegance. I leave my final, recursive implementation here, to help others searching a small implementation.


Convert Roman Numerals by Recursion

  • The algorithm is able to handle numerals in irregular subtractive notation (f.e. XIIX).
  • This implementation may only work with well-formed (strings matching /[mdclxvi]*/i) roman numerals.
  • The implementation is not optimized for speed.
// returns the value for a roman literal
private static int romanValue(int index)
{
    int basefactor = ((index % 2) * 4 + 1); // either 1 or 5...
    // ...multiplied with the exponentation of 10, if the literal is `x` or higher
    return index > 1 ? (int) (basefactor * System.Math.Pow(10.0, index / 2)) : basefactor;
}

public static int FromRoman(string roman)
{
    roman = roman.ToLower();
    string literals = "mdclxvi";
    int value = 0, index = 0;
    foreach (char literal in literals)
    {
        value = romanValue(literals.Length - literals.IndexOf(literal) - 1);
        index = roman.IndexOf(literal);
        if (index > -1)
            return FromRoman(roman.Substring(index + 1)) + (index > 0 ? value - FromRoman(roman.Substring(0, index)) : value);
    }
    return 0;
}

Try it using this .Netfiddle: https://dotnetfiddle.net/veaNk3

How does it work?

This algorithm calculates the value of a Roman Numeral by taking the highest value from the Roman Numeral and adding/subtracting recursively the value of the remaining left/right parts of the literal.

ii X iiv # Pick the greatest value in the literal `iixiiv` (symbolized by uppercase)

Then recursively reevaluate and subtract the lefthand-side and add the righthand-side:

(iiv) + x - (ii) # Subtract the lefthand-side, add the righthand-side
(V - (ii)) + x - ((I) + i) # Pick the greatest values, again
(v - ((I) + i)) + x - ((i) + i) # Pick the greatest value of the last numeral compound

Finally the numerals are substituted by their integer values:

(5 - ((1) + 1)) + 10 - ((1) + 1)
(5 - (2)) + 10 - (2)
3 + 10 - 2
= 11
Brazilin answered 7/12, 2017 at 12:52 Comment(8)
This is the only algorithm that handles all cases, even special historic versions of numbers like XIIX and IIXX for 18 or XXL for 30. Thanks for this elegant method!Sweettalk
I ended up using a replacement function for GetValue, used a dictionary instead as it seemed a lot more "sensible" to me,although your method probably works better/has reasons for it. I didn't like that the first function is using the index of the character inside of "mdclxvi" to get the value. That said. This function is really awesome. I way prefer it to the others that are here, this is great.Creaky
Thanks @Creaky - i ended up using a String since i wanted the snippet to be as small as possible. That's also the reason why the method name is probably not the best. In fact romanValue's index references implicitly to indices of the roman numeral alphabet which reduces the readability of the algorithm.Brazilin
Errr.. are you sure this works? 2074 = MMLXXIV. However, according to this algorithm, MMLXXIV = 2182! It might be me, but....Grajeda
Thanks for the hint - i will have a look these days...Brazilin
@Grajeda i can't reproduce your result. How did you execute the algorithm?Brazilin
@FlorianNeumann - apologies - but I've moved on since. Like I said - it might have been me, but just there in case anyone else wants to test it.Grajeda
No worries - i added your example to the fiddle above. :)Brazilin
C
4
private static int convertRomanToInt(String romanNumeral)
{
    Dictionary<Char, Int32> romanMap = new Dictionary<char, int>
    {
        {'I', 1 },
        {'V', 5},
        {'X', 10},
        {'L', 50},
        {'C', 100},
        {'D', 500},
        {'M', 1000}
    };

    Int32 result = 0;
    for (Int32 index = romanNumeral.Length - 1, last = 0; index >= 0; index--)
    {
        Int32 current = romanMap[romanNumeral[index]];
        result += (current < last ? -current : current);
        last = current;
    }

    return result;
}
Coriolanus answered 18/3, 2021 at 0:11 Comment(0)
U
3

I wrote a simple Roman Numeral Converter just now, but it doesn't do a whole lot of error checking, but it seems to work for everything I could throw at it that is properly formatted.

public class RomanNumber
{
    public string Numeral { get; set; }
    public int Value { get; set; }
    public int Hierarchy { get; set; }
}

public List<RomanNumber> RomanNumbers = new List<RomanNumber>
    {
        new RomanNumber {Numeral = "M", Value = 1000, Hierarchy = 4},
        //{"CM", 900},
        new RomanNumber {Numeral = "D", Value = 500, Hierarchy = 4},
        //{"CD", 400},
        new RomanNumber {Numeral = "C", Value = 100, Hierarchy = 3},
        //{"XC", 90},
        new RomanNumber {Numeral = "L", Value = 50, Hierarchy = 3},
        //{"XL", 40},
        new RomanNumber {Numeral = "X", Value = 10, Hierarchy = 2},
        //{"IX", 9},
        new RomanNumber {Numeral = "V", Value = 5, Hierarchy = 2},
        //{"IV", 4},
        new RomanNumber {Numeral = "I", Value = 1, Hierarchy = 1}
    };

/// <summary>
/// Converts the roman numeral to int, assumption roman numeral is properly formatted.
/// </summary>
/// <param name="romanNumeralString">The roman numeral string.</param>
/// <returns></returns>
private int ConvertRomanNumeralToInt(string romanNumeralString)
{
    if (romanNumeralString == null) return int.MinValue;

    var total = 0;
    for (var i = 0; i < romanNumeralString.Length; i++)
    {
        // get current value
        var current = romanNumeralString[i].ToString();
        var curRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == current.ToUpper());

        // last number just add the value and exit
        if (i + 1 == romanNumeralString.Length)
        {
            total += curRomanNum.Value;
            break;
        } 

        // check for exceptions IV, IX, XL, XC etc
        var next = romanNumeralString[i + 1].ToString();
        var nextRomanNum = RomanNumbers.First(rn => rn.Numeral.ToUpper() == next.ToUpper());

        // exception found
        if (curRomanNum.Hierarchy == (nextRomanNum.Hierarchy - 1))
        {
            total += nextRomanNum.Value - curRomanNum.Value;
            i++;
        }
        else
        {
            total += curRomanNum.Value;
        }
    }


    return total;
}
Ultraconservative answered 15/2, 2013 at 20:49 Comment(0)
F
2

With Humanizer library you can change Roman numerals to numbers using the FromRoman extension

"IV".FromRoman() => 4

And reverse operation using the ToRoman extension.

4.ToRoman() => "IV"
Fluorspar answered 17/4, 2021 at 9:5 Comment(0)
D
1

Borrowed a lot from System.Linq on this one. String implements IEnumerable<char>, so I figured that was appropriate since we are treating it as an enumerable object anyways. Tested it against a bunch of random numbers, including 1, 3, 4, 8, 83, 99, 404, 555, 846, 927, 1999, 2420.

    public static IDictionary<char, int> CharValues 
    { 
        get 
        { 
            return new Dictionary<char, int>
            {{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}};
        } 
    }

    public static int RomanNumeralToInteger(IEnumerable<char> romanNumerals)
    {
        int retVal = 0;

        //go backwards
        for (int i = romanNumerals.Count() - 1; i >= 0; i--)
        {
            //get current character
            char c = romanNumerals.ElementAt(i);

            //error checking
            if (!CharValues.ContainsKey(c)) throw new InvalidRomanNumeralCharacterException(c);

            //determine if we are adding or subtracting
            bool op = romanNumerals.Skip(i).Any(rn => CharValues[rn] > CharValues[c]);

            //then do so
            retVal = op ? retVal - CharValues[c] : retVal + CharValues[c];
        }

        return retVal;
    }
Deneb answered 17/11, 2015 at 6:31 Comment(0)
D
1

Solution with fulfilling the "subtractive notation" semantics checks

None of the current solutions completely fulfills the entire set of rules for the "subtractive notation". "IIII" -> is not possible. Each of the solutions results a 4. Also the strings: "CCCC", "VV", "IC", "IM" are invalid.

A good online-converter to check the semantics is https://www.romannumerals.org/converter So, if you really want doing a completely semantics-check, it's much more complex.

My approach was to first write the unit tests with the semantic checks. Then to write the code. Then to reduce the loops with some linq expressions.

Maybe there is a smarter solution, but I think the following code fullfills the rules to convert a roman numerals string.

After the code section, there is a section with my unit tests.

public class RomanNumerals
{

    private List<Tuple<char, ushort, char?[]>> _validNumerals = new List<Tuple<char, ushort, char?[]>>()
    {
        new Tuple<char, ushort, char?[]>('I', 1, new char? [] {'V', 'X'}),
        new Tuple<char, ushort, char?[]>('V', 5, null),
        new Tuple<char, ushort, char?[]>('X', 10, new char?[] {'L', 'C'}),
        new Tuple<char, ushort, char?[]>('L', 50, null),
        new Tuple<char, ushort, char?[]>('C', 100, new char? [] {'D', 'M'}),
        new Tuple<char, ushort, char?[]>('D', 500, null),
        new Tuple<char, ushort, char?[]>('M', 1000, new char? [] {null, null})
    };


    public int TranslateRomanNumeral(string input)
    {
        var inputList = input?.ToUpper().ToList();

        if (inputList == null || inputList.Any(x => _validNumerals.Select(t => t.Item1).Contains(x) == false))
        {
            throw new ArgumentException();
        }

        char? valForSubtraction = null;
        int result = 0;
        bool noAdding = false;
        int equalSum = 0;
        for (int i = 0; i < inputList.Count; i++)
        {
            var currentNumeral = _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i]);
            var nextNumeral = i < inputList.Count - 1 ? _validNumerals.FirstOrDefault(s => s.Item1 == inputList[i + 1]) : null;
            bool currentIsDecimalPower = currentNumeral?.Item3?.Any() ?? false;

            if (nextNumeral != null)
            {
                // Syntax and Semantics checks
                if ((currentNumeral.Item2 < nextNumeral.Item2) && (currentIsDecimalPower == false || currentNumeral.Item3.Any(s => s == nextNumeral.Item1) == false) ||
                    (currentNumeral.Item2 == nextNumeral.Item2) && (currentIsDecimalPower == false || nextNumeral.Item1 == valForSubtraction) ||
                    (currentIsDecimalPower && result > 0 &&  ((nextNumeral.Item2 -currentNumeral.Item2) > result )) ||
                    (currentNumeral.Item2 > nextNumeral.Item2) && (nextNumeral.Item1 == valForSubtraction)

                    )
                {
                    throw new ArgumentException();
                }

                if (currentNumeral.Item2 == nextNumeral.Item2)
                {
                    equalSum += equalSum == 0 ? currentNumeral.Item2 + nextNumeral.Item2 : nextNumeral.Item2;
                    int? smallest = null;
                    var list = _validNumerals.Where(p => _validNumerals.FirstOrDefault(s => s.Item1 == currentNumeral.Item1).Item3.Any(s2 => s2 != null && s2 == p.Item1)).ToList();
                    if (list.Any())
                    {
                        smallest = list.Select(s3 => s3.Item2).ToList().Min();
                    }

                    // Another Semantics check
                    if (currentNumeral.Item3 != null && equalSum >= (smallest - currentNumeral.Item2))
                    {
                        throw new ArgumentException();
                    }

                    result += noAdding ? 0 : currentNumeral.Item2 + nextNumeral.Item2;
                    noAdding = !noAdding;
                    valForSubtraction = null;
                }
                else
                if (currentNumeral.Item2 < nextNumeral.Item2)
                {
                    equalSum = 0;
                    result += nextNumeral.Item2 - currentNumeral.Item2;
                    valForSubtraction = currentNumeral.Item1;
                    noAdding = true;
                }
                else 
                if (currentNumeral.Item2 > nextNumeral.Item2)
                {
                    equalSum = 0;
                    result += noAdding ? 0 : currentNumeral.Item2;
                    noAdding = false;

                    valForSubtraction = null;
                }
            }
            else
            {
                result += noAdding ? 0 : currentNumeral.Item2;
            }
        }
        return result;
    }
}

Here are the UNIT tests

    [TestFixture]
public class RomanNumeralsTests
{
    [Test]
    public void TranslateRomanNumeral_WhenArgumentIsNull_RaiseArgumentNullException()
    {
        var romanNumerals = new RomanNumerals();

        Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(null));
    }

    [TestCase("A")]
    [TestCase("-")]
    [TestCase("BXA")]
    [TestCase("MMXK")]
    public void TranslateRomanNumeral_WhenInvalidNumeralSyntax_RaiseException(string input)
    {
        var romanNumerals = new RomanNumerals();

        Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
    }

    [TestCase("IIII")]
    [TestCase("CCCC")]
    [TestCase("VV")]
    [TestCase("IC")]
    [TestCase("IM")]
    [TestCase("XM")]
    [TestCase("IL")]
    [TestCase("MCDXCXI")]
    [TestCase("MCDDXC")]
    public void TranslateRomanNumeral_WhenInvalidNumeralSemantics_RaiseException(string input)
    {
        var romanNumerals = new RomanNumerals();

        Assert.Throws<ArgumentException>(() => romanNumerals.TranslateRomanNumeral(input));
    }


    [TestCase("I", 1)]
    [TestCase("II", 2)]
    [TestCase("III", 3)]
    [TestCase("IV", 4)]
    [TestCase("XLII", 42)]
    [TestCase("MMXIII", 2013)]
    [TestCase("MXI", 1011)]
    [TestCase("MCDXCIX", 1499)]
    [TestCase("MMXXII", 2022)]
    [TestCase("V", 5)]
    [TestCase("VI", 6)]
    [TestCase("CX", 110)]
    [TestCase("CCCLXXV", 375)]
    [TestCase("MD", 1500)]
    [TestCase("MDLXXV", 1575)]
    [TestCase("MDCL", 1650)]
    [TestCase("MDCCXXV", 1725)]
    [TestCase("MDCCC", 1800)]
    [TestCase("MDCCCLXXV", 1875)]
    [TestCase("MCML", 1950)]
    [TestCase("MMXXV", 2025)]
    [TestCase("MMC", 2100)]
    [TestCase("MMCLXXV", 2175)]
    [TestCase("MMCCL", 2250)]
    [TestCase("MMCCCXXV", 2325)]
    [TestCase("MMCD", 2400)]
    [TestCase("MMCDLXXV", 2475)]
    [TestCase("MMDL", 2550)]
    [TestCase("MMMMMMMM", 8000)]
    [TestCase("MMMMMMMMIV", 8004)]
    public void TranslateRomanNumeral_WhenValidNumeral_Translate(string input, int output)
    {
        var romanNumerals = new RomanNumerals();

        var result = romanNumerals.TranslateRomanNumeral(input);

        Assert.That(result.Equals(output));
    }
}
Dashtilut answered 13/3, 2019 at 16:49 Comment(3)
How is "IIII" not possible? Using the "subtractive notation" is an additional feature but non-subtractive notations are already in use.Brazilin
That's correct. The "subtractive notation" is only one possibility. So, consider my solution just for the "subtractive notation". Maybe, out there are requirements to convert and check strict with the rules for that notation.Dashtilut
Thank you for changing the scope of your answer!Brazilin
T
1
    private static Dictionary<char, int> RomanNumberMap = new Dictionary<char, int> {
        {'I', 1},
        {'V', 5},
        {'X', 10},
        {'L', 50},
        {'C', 100},
        {'D', 500},
        {'M', 1000}
    };

    private const string RomanNumberValidationRegEx = "^(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$";

    private static int ConvertToRomanNumberToInteger(string romanNumber)
    {
        if (!Regex.IsMatch(romanNumber, RomanNumberValidationRegEx))
        {
            throw new ArgumentOutOfRangeException(romanNumber);
        }

        int result = 0;

        for (int i = 0; i < romanNumber.Length; i++)
        {
            int currentVal = RomanNumberMap[romanNumber[i]];

            if (romanNumber.Length > i + 1)
            {
                int nextVal = RomanNumberMap[romanNumber[i + 1]];

                if (nextVal > currentVal)
                {
                    result = result + (nextVal - currentVal);
                    i++;
                    continue;
                }

            }

            result = result + currentVal;

        }

        return result;
    }
Tanberg answered 16/2, 2021 at 21:16 Comment(0)
B
0

I will suggest a simplest method for this by using array in .net : comments are given in C# section for explanation

VB.net

Public Class Form1
    Dim indx() As Integer = {1, 2, 3, 4, 5, 10, 50, 100, 500, 1000}
    Dim row() As String = {"I", "II", "III", "IV", "V", "X", "L", "C", "D", "M"}
    Dim limit As Integer = 9
    Dim output As String = ""
    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim num As Integer
        output = ""
        num = CInt(txt1.Text)
        While num > 0
            num = find(num)
        End While
        txt2.Text = output
    End Sub
    Public Function find(ByVal Num As Integer) As Integer
        Dim i As Integer = 0
        While indx(i) <= Num
            i += 1
        End While
        If i <> 0 Then
            limit = i - 1
        Else
            limit = 0
        End If
        output = output & row(limit)
        Num = Num - indx(limit)
        Return Num
    End Function
End Class

C#

using Microsoft.VisualBasic;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
public class Form1
{
    int[] indx = {
        1,
        2,
        3,
        4,
        5,
        10,
        50,
        100,
        500,
        1000
        // initialize array of integers 
    };
    string[] row = {
        "I",
        "II",
        "III",
        "IV",
        "V",
        "X",
        "L",
        "C",
        "D",
        "M"
        //Carasponding roman letters in for the numbers in the array
    };
        // integer to indicate the position index for link two arrays 
    int limit = 9;
        //string to store output
    string output = "";
    private void Button1_Click(System.Object sender, System.EventArgs e)
    {
        int num = 0;
        // stores the input 
        output = "";
        // clear output before processing
        num = Convert.ToInt32(txt1.Text);
        // get integer value from the textbox
        //Loop until the value became 0
        while (num > 0) {
            num = find(num);
            //call function for processing
        }
        txt2.Text = output;
        // display the output in text2
    }
    public int find(int Num)
    {
        int i = 0;
        // loop variable initialized with 0
        //Loop until the indx(i).value greater than or equal to num
        while (indx(i) <= Num) {
            i += 1;
        }
        // detemine the value of limit depends on the itetration
        if (i != 0) {
            limit = i - 1;
        } else {
            limit = 0;
        }
        output = output + row(limit);
        //row(limit) is appended with the output
        Num = Num - indx(limit);
        // calculate next num value
        return Num;
        //return num value for next itetration 
    }
}
Burgoo answered 22/7, 2014 at 5:25 Comment(0)
D
0

I refer from this blog. You could just reverse the roman numeral , then all the thing would be more easier compare to make the comparison.
public static int pairConversion(int dec, int lastNum, int lastDec) { if (lastNum > dec) return lastDec - dec; else return lastDec + dec; }

    public static int ConvertRomanNumtoInt(string strRomanValue)
    {
        var dec = 0;
        var lastNum = 0;
        foreach (var c in strRomanValue.Reverse())
        {
            switch (c)
            {
                case 'I':
                    dec = pairConversion(1, lastNum, dec);
                    lastNum = 1;
                    break;
                case 'V':
                    dec=pairConversion(5,lastNum, dec);
                    lastNum = 5;
                    break;
                case 'X':
                    dec = pairConversion(10, lastNum, dec);
                    lastNum = 10;
                    break;
                case 'L':
                    dec = pairConversion(50, lastNum, dec);
                    lastNum = 50;
                    break;
                case 'C':
                    dec = pairConversion(100, lastNum, dec);
                    lastNum = 100;
                    break;
                case 'D':
                    dec = pairConversion(500, lastNum, dec);
                    lastNum = 500;
                    break;
                case 'M':
                    dec = pairConversion(1000, lastNum, dec);
                    lastNum = 1000;
                    break;
            }
        }
        return dec;

    }
Deodand answered 17/10, 2015 at 18:40 Comment(0)
U
0

This one uses a stack:

    public int RomanToInt(string s)
    {
        var dict = new Dictionary<char, int>();
        dict['I'] = 1;
        dict['V'] = 5;
        dict['X'] = 10;
        dict['L'] = 50;
        dict['C'] = 100;
        dict['D'] = 500;
        dict['M'] = 1000;
        Stack<char> st = new Stack<char>();
        foreach (char ch in s.ToCharArray())
            st.Push(ch);

        int result = 0;
        while (st.Count > 0)
        {
            var c1=st.Pop();
            var ch1 = dict[c1];

            if (st.Count > 0)
            {
                var c2 = st.Peek();
                var ch2 = dict[c2];
                if (ch2 < ch1)
                {
                    result += (ch1 - ch2);
                    st.Pop();
                }
                else
                {
                    result += ch1;
                }
            }
            else
            {
                result += ch1;
            }
        }
        return result;
    }
Undo answered 26/1, 2016 at 21:5 Comment(0)
S
0

I wrote this just using arrays.
I omit the testing code here, but it looks it works properly.

public static class RomanNumber {
        static string[] units = { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" };
        static string[] tens = { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" };
        static string[] hundreds = { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" };
        static string[] thousands = { "", "M", "MM", "MMM" };

        static public bool IsRomanNumber(string source) {
            try {
                return RomanNumberToInt(source) > 0;
            }
            catch {
                return false;
            }
        }

        /// <summary>
        /// Parses a string containing a roman number.
        /// </summary>
        /// <param name="source">source string</param>
        /// <returns>The integer value of the parsed roman numeral</returns>
        /// <remarks>
        /// Throws an exception on invalid source.
        /// Throws an exception if source is not a valid roman number.
        /// Supports roman numbers from "I" to "MMMCMXCIX" ( 1 to 3999 )
        /// NOTE : "IMMM" is not valid</remarks>
        public static int RomanNumberToInt(string source) {
            if (String.IsNullOrWhiteSpace(source)) {
                throw new ArgumentNullException();
            }

            int total = 0;
            string buffer = source;

            // parse the last four characters in the string
            // each time we check the buffer against a number array,
            // starting from units up to thousands
            // we quit as soon as there are no remaing characters to parse

            total += RipOff(buffer, units, out buffer);

            if (buffer != null) {
                total += (RipOff(buffer, tens, out buffer)) * 10;
            }

            if (buffer != null) {
                total += (RipOff(buffer, hundreds, out buffer)) * 100;
            }

            if (buffer != null) {
                total += (RipOff(buffer, thousands, out buffer)) * 1000;
            }

            // after parsing for thousands, if there is any character left, this is not a valid roman number
            if (buffer != null) {
                throw new ArgumentException(String.Format("{0} is not a valid roman number", buffer));
            }
            return total;
        }


        /// <summary>
        /// Given a string, takes the four characters on the right,
        /// search an element in the numbers array and returns the remaing characters.
        /// </summary>
        /// <param name="source">source string to parse</param>
        /// <param name="numbers">array of roman numerals</param>
        /// <param name="left">remaining characters on the left</param>
        /// <returns>If it finds a roman numeral returns its integer value; otherwise returns zero</returns>
        public static int RipOff(string source, string[] numbers, out string left) {
            int result = 0;

            string buffer = null;

            // we take the last four characters : this is the length of the longest numeral in our arrays
            // ("VIII", "LXXX", "DCCC")
            // or all if source length is 4 or less
            if (source.Length > 4) {
                buffer = source.Substring(source.Length - 4);
                left = source.Substring(0, source.Length - 4);
            }
            else {
                buffer = source;
                left = null;
            }

            // see if buffer exists in the numbers array 
            // if it does not, skip the first character and try again
            // until buffer contains only one character
            // append the skipped character to the left arguments
            while (!numbers.Contains(buffer)) {
                if (buffer.Length == 1) {
                    left = source; // failed
                    break;
                }
                else {
                    left += buffer.Substring(0, 1);
                    buffer = buffer.Substring(1);
                }
            }

            if (buffer.Length > 0) {
                if (numbers.Contains(buffer)) {
                    result = Array.IndexOf(numbers, buffer);
                }
            }

            return result;
        }
    }
}

EDIT
Forget about it !
Just look at BrunoLM solution here.
It's simple and elegant.
The only caveat is that it does not check the source.

Stoner answered 17/2, 2016 at 17:27 Comment(0)
M
0

This is my solution:

    /// <summary>
    /// Converts a Roman number string into a Arabic number
    /// </summary>
    /// <param name="romanNumber">the Roman number string</param>
    /// <returns>the Arabic number (0 if the given string is not convertible to a Roman number)</returns>
    public static int ToArabicNumber(string romanNumber)
    {
        string[] replaceRom = { "CM", "CD", "XC", "XL", "IX", "IV" };
        string[] replaceNum = { "DCCCC", "CCCC", "LXXXX", "XXXX", "VIIII", "IIII" };
        string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
        int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        return Enumerable.Range(0, replaceRom.Length)
            .Aggregate
            (
                romanNumber,
                (agg, cur) => agg.Replace(replaceRom[cur], replaceNum[cur]),
                agg => agg.ToArray()
            )
            .Aggregate
            (
                0,
                (agg, cur) =>
                {
                    int idx = Array.IndexOf(roman, cur.ToString());
                    return idx < 0 ? 0 : agg + arabic[idx];
                },
                agg => agg
            );
    }

    /// <summary>
    /// Converts a Arabic number into a Roman number string
    /// </summary>
    /// <param name="arabicNumber">the Arabic number</param>
    /// <returns>the Roman number string</returns>
    public static string ToRomanNumber(int arabicNumber)
    {
        string[] roman = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
        int[] arabic = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
        return Enumerable.Range(0, arabic.Length)
            .Aggregate
            (
                Tuple.Create(arabicNumber, string.Empty),
                (agg, cur) =>
                {
                    int remainder = agg.Item1 % arabic[cur];
                    string concat = agg.Item2 + string.Concat(Enumerable.Range(0, agg.Item1 / arabic[cur]).Select(num => roman[cur]));
                    return Tuple.Create(remainder, concat);
                },
                agg => agg.Item2
            );
    }

Here's the Explanation how the methods work:

ToArabicNumber

First aggregation step is to Replace the Roman Number special cases (e.g.: IV -> IIII). Second Aggregate step simply sums up the equivalent Arabic number of the Roman letter (e.g. V -> 5)

ToRomanNumber:

I start the aggregation with the given Arabic number. For each step the number will be divided by the equivalent number of the Roman letter. The remainder of this division is then the input for the next step. The division Result will be translated to the Equivalent Roman Number character which will be appended to the result string.

Moriyama answered 3/4, 2017 at 12:33 Comment(1)
Thanks for your answer - please also add some short explanation that sums up your solution.Ency
Y
0
private static HashMap<Character, Integer> romanMap = new HashMap<>() {{
    put('I', 1); put('V', 5); put('X', 10); put('L', 50); 
    put('C', 100); put('D', 500); put('M', 1000);
}};

private static int convertRomanToInt(String romanNumeral) {
    int total = 0;
    romanNumeral = romanNumeral.toUpperCase();

    //add every Roman numeral
    for(int i = 0; i < romanNumeral.length(); i++) {
        total += romanMap.get(romanNumeral.charAt(i));
    }

    //remove the Roman numerals that are followed 
    //directly by a larger Roman numeral
    for(int i = 0; i < romanNumeral.length()-1; i++) {
        if(romanMap.get(romanNumeral.charAt(i)) 
           < romanMap.get(romanNumeral.charAt(i+1))) {
           total -= 2* romanMap.get(romanNumeral.charAt(i));
        }
    }
    return total;
}

//note that the topmost solution does not solve this Roman numeral
//but mine does
//also note that this solution is a preference of simplicity over complexity
public static void main(String[] args) {
    String rn = "CcLXxiV"; //274
    System.out.println("Convert " + rn + " to " + convertRomanToInt(rn));
}
Yingling answered 31/7, 2019 at 21:52 Comment(0)
A
0
/*
this uses the string object Replace() & Split() methods
*/
int ToNumber(string roman){
    /*
    the 0 padding after the comma delimiter allows adding up the extra index produced by Split, which is not numerical
    */
    string s1=roman.Replace("CM","900,0");
    s1=s1.Replace("M","1000,0");
    s1=s1.Replace("CD","400,0");
    s1=s1.Replace("D","500,0");
    s1=s1.Replace("XC","90,0");
    s1=s1.Replace("C","100,0");
    s1=s1.Replace("XL","40,0");
    s1=s1.Replace("L","50,0");
    s1=s1.Replace("IX","9,0");
    s1=s1.Replace("X","10,0");
    s1=s1.Replace("IV","4,0");
    s1=s1.Replace("V","5,0");
    s1=s1.Replace("I","1,0");

    string[] spl=s1.Split(",");
    int rlt=0;
    for(int i=0;i<spl.Count();i++)
    {
        rlt+= Convert.ToInt32(spl[i].ToString());
    }               
    return rlt;
}
Albeit answered 26/1, 2021 at 17:11 Comment(0)
T
0

Here is my O(n) solution in JavaScript

const NUMERAL = {
    'I': 1,
    'V': 5,
    'X': 10,
    'L': 50,
    'C': 100,
    'D': 500,
    'M': 1000,
}

var romanToInt = function(s) {
    if (s.length === 1) return +NUMERAL[s];

    let number = 0;
    for (let i = 0; i < s.length; i++) {
        let num = +NUMERAL[s[i]];
        let prev = +NUMERAL[s[i-1]];
        
        if (prev < num) number += num - (2 * prev);
        else number += num;
    }
    
    return number;
};
Tchad answered 28/9, 2021 at 12:18 Comment(0)
C
0

FWIW, here is a "try parse" version of David DeMar's answer:

private static readonly Dictionary<char, int> _romanMap = new Dictionary<char, int>() { { 'I', 1 }, { 'V', 5 }, { 'X', 10 }, { 'L', 50 }, { 'C', 100 }, { 'D', 500 }, { 'M', 1000 } };

public static bool TryParseRoman(string text, out int value)
{
    value = 0;
    if (string.IsNullOrEmpty(text))
        return false;

    var number = 0;
    for (var i = 0; i < text.Length; i++)
    {
        if (!_romanMap.TryGetValue(text[i], out var num))
            return false;

        if ((i + 1) < text.Length)
        {
            if (!_romanMap.TryGetValue(text[i + 1], out var num2))
                return false;

            if (num < num2)
            {
                number -= num;
                continue;
            }
        }

        number += num;
    }

    value = number;
    return true;
}
Counterword answered 18/1, 2022 at 14:37 Comment(0)
H
0
public class Solution {
public int RomanToInt(string s) {
    var dict = new Dictionary<char,int>{
        {'I',1},
        {'V',5},
        {'X',10},
        {'L',50},
        {'C',100},
        {'M',1000},
        {'D',500}, 
    };

    var result = 0; //What am I gonna return it from method  ?
    for(int i=0; i<s.Length; i++)
    {
        if(i+1<s.Length && dict[s[i]] < dict[s[i+1]])
        {
            result -=dict[s[i]];

        }
        else
        {
             result +=dict[s[i]];

        }
    }
    return result;
    
}
}
Heterocercal answered 12/11, 2022 at 14:5 Comment(0)
C
0

// Convert Roman Number to Integer

// M : 1000, D : 500 , C : 100 , L :50 X : 10 , V : 5, I : I
// XVIII = 10 + 5 + 1 + 1 + 1 = 18 
// VL = 5 + 50 = 55

// X L 
// when the digit of lower value is befor higher value then the lower value is subtracted from higher value
// - 10 + 50 = 40 


function romanToInteger(romanStr) {

    let num = 0;
    let objRoman = {M:1000,D:500,C:100,L:50,X:10,V:5,I:1};

    for (let i = 0; i < romanStr.length; i++) {
        if(objRoman[romanStr[i]] < objRoman[romanStr[i+1]]) {
            num -= objRoman[romanStr[i]]; 
        } else {
            num += objRoman[romanStr[i]]; 
        }
    }
    return num;
}


console.log(romanToInteger("XL"));
console.log(romanToInteger("XVIII"));
Conchiferous answered 5/5, 2023 at 7:14 Comment(0)
C
0

Here's the smallest (if not the simplest) approach I could think of.

Start with these two helper structures:

private (string from, string to)[] _expand = new[]
{
    (from: "IV", to: "IIII"),
    (from: "IX", to: "VIIII"),
    (from: "XL", to: "XXXX"),
    (from: "XC", to: "LXXXX"),
    (from: "CD", to: "CCCC"),
    (from: "CM", to: "DCCCC"),
};

private Dictionary<char, int> _values = new[]
{
    (letter: 'I', value: 1),
    (letter: 'V', value: 5),
    (letter: 'X', value: 10),
    (letter: 'L', value: 50),
    (letter: 'C', value: 100),
    (letter: 'D', value: 500),
    (letter: 'M', value: 1000),
}.ToDictionary(x => x.letter, x => x.value);

Now it is super simple with a couple of uses of Aggregate:

public int RomanToInteger(string text) =>
    _expand
        .Aggregate(text, (a, x) => a.Replace(x.from, x.to))
        .Aggregate(0, (a, x) => a + _values[x]);

When I call RomanToInteger("MCMXLIX") I get 1949 as expected.

Crapulous answered 11/6, 2023 at 11:8 Comment(0)
I
-1
public int RomanToInt(string s)
{
    char[] romans = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
    int[] nums = { 1, 5, 10, 50, 100, 500, 1000 };

    int result = 0;

    foreach (char c in s)
    {
        for (int i = 0; i < romans.Length; i++)
        {
            if (romans[i] == c)
            {
                result += nums[i];
            }
        }
    }

    if (s.Contains("IV") || s.Contains("IX"))
        result -= 2;

    if (s.Contains("XL") || s.Contains("XC"))
        result -= 20;

    if (s.Contains("CD") || s.Contains("CM"))
        result -= 200;

    return result;
}
Illconditioned answered 21/7, 2022 at 6:15 Comment(1)
You realize, the exact same answer is already up there for more than 5 years?Axletree
A
-4
public static int ConvertRomanNumtoInt(string strRomanValue)
{
    Dictionary RomanNumbers = new Dictionary
    {
        {"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}
    };
    int retVal = 0;
    foreach (KeyValuePair pair in RomanNumbers)
    {
        while (strRomanValue.IndexOf(pair.Key.ToString()) == 0)
        {
            retVal += int.Parse(pair.Value.ToString());
            strRomanValue = strRomanValue.Substring(pair.Key.ToString().Length);
        }
    }
    return retVal;
}
Aikens answered 15/2, 2013 at 18:12 Comment(7)
This function can count up to three before failing on IV.Perri
See ideone.com/kCEjsE – I just changed the dictionary to be generic and moved the dictionary out of the function.Grannias
The order of items in a Dictionary are non-deterministic, so it's possible for it to happen to work, if you're lucky, on some implementations, if "IV" happens to be spit out before "I", but if it's not, you're hosed. Next, "VI" doesn't even work. If the "I" key is returned before "V" then it will parse as zero, not six.Perri
You also bring up another point; there is no non-generic dictionary in C#, so it doesn't even compile.Perri
Your code example just shows what makes this so dangerous. It sometimes works, but it doesn't always work, even with the same input, because you're relying on indeterminate behavior. The exact same code, with the exact same input, can just stop working. Now if you had a List<KeyValuePair> it might work; I'd need to look a bit deeper, although it's most certainly not an ideal solution.Perri
Well, you could easily replace that dictionary with a list of tuples which you iterate once. The lookup functionality of the dictionary is not needed here and you only need the pairing.Grannias
@Grannias Correct. I'd also remove the constant substring calls and use the overload of IndexOf that takes an index.Perri

© 2022 - 2024 — McMap. All rights reserved.