📜 ⬆️ ⬇️

LINQ converter between roman and arabic numbers

It turned out that he was sorting out LINQ with students, and with schoolchildren a transformation between the Roman and Arabic number systems. As a result, the thoughts got lost and an interesting converter code turned out, which is a pity to bury in local archives. An example will fit in as an exercise for understanding LINQ.

So, we need a dictionary that matches the elements of Roman numbers with Arabic.

Dictionary<int, string> ra = new Dictionary<int, string> { { 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" } }; 

Using this dictionary you can write converters from Roman to Arabic notation and back. For brevity and beauty, we will not process preconditions. For Arabic numerals, the allowable range is defined as [1.4000). And Roman numerals must be correct and converted to upper case.

Arab to Roman Converter


Parsing numbers start with large numbers. The keys in the dictionary are ordered in descending order. Looking through them in a given order, we find the first key with a value of no more than a convertible number (Where operator). In the dictionary, this number is associated with the letter combination of the Roman numeral system. We glue the found letter combination with the recursively converted remainder of the number (Select operator). The first result will be the desired representation of a number in Roman notation (operator FirstOrDefault).
')
 string ToRoman(int number) => ra .Where(d => number >= d.Key) .Select(d => d.Value + ToRoman(number - d.Key)) .FirstOrDefault(); 

Converter from roman to arab


Analysis of the Roman number is from left to right. We look through all the alphabetic combinations from the dictionary to the beginning of the converted string number.StartsWith (d.Value). If there is a match, the key field will contain a numeric value corresponding to an alphabetic combination. The resulting value is summed with the recursively processed remaining line.

 int ToArabic(string number) => number.Length == 0 ? 0 : ra .Where(d => number.StartsWith(d.Value)) .Select(d => d.Key + ToArabic(number.Substring(d.Value.Length))) .FirstOrDefault(); 

Ready class for copy-pasting v1
 static class RomanNum { // (c) 2015, Alexey Danov | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY ... static Dictionary<int, string> ra = new Dictionary<int, string> { { 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" } }; public static string ToRoman(int number) => ra .Where(d => number >= d.Key) .Select(d => d.Value + ToRoman(number - d.Key)) .FirstOrDefault(); public static int ToArabic(string number) => number.Length == 0 ? 0 : ra .Where(d => number.StartsWith(d.Value)) .Select(d => d.Key + ToArabic(number.Substring(d.Value.Length))) .First(); } 

On this one could stop. But it turns out that the number 499 can be written in five ways: CDXCIX, LDVLIV, XDIX, VDIV or ID. For a short record can be used not specified in the dictionary combinations. You can expand the dictionary, and you can build on only the original letters IVXLCDM, which correspond to the numbers (1,5,10,50,100,500,1000).

In order to upgrade the skills of the .Net mage VC-level (VC == 95), the dictionary will be automatically generated. And the resulting code can be used to test the skills of reading someone else's code on the entrance tests to the .Net school of IC-level magicians.

Converter from roman to arabic with strapping


To generate a dictionary, we had to use an external variable. Further the code without comments.

 int o = 1; string w = "IVXLCDM"; Dictionary<char, int> ra = w.ToDictionary(ch => ch, ch => (o = ("" + o)[0] == '1' ? o * 2 : o * 5) / 2); int ToArabic(string num) => num.Select((c, i) => ++i < num.Length && ra[c] < ra[num[i]] ? -ra[c] : ra[c]).Sum(); 

Arab to Roman converter with strapping


But for the transfer to the Roman system for brevity, it took a couple of auxiliary functions. You can include them in the function itself, but you get a jumble.

 string W(int k, int l = 1) => w.Substring(k, l); string R(char m, int k) => m == '9' ? W(k-2)+W(k) : m == '5' ? W(k-1) : m == '4' ? W(k-2, 2) : W(k-2); string ToRoman(int num) => num < 1 ? "" : (from z in "000100101".Split('1') from m in "9541" select m + z) .Where(z => num >= (o = int.Parse(z))) .Select(z => R(z[0], z.Length * 2)).First() + ToRoman(num - o); 


Ready class for copy-pasting v2
 static class RomanNumEx { // (c) 2015, Alexey Danov | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY ... static int o = 1; static string w = "IVXLCDM"; static Dictionary<char, int> ra = w.ToDictionary(ch => ch, ch => (o = ("" + o)[0] == '1' ? o * 2 : o * 5) / 2); public static int ToArabic(string num) => num .Select((c, i) => ++i < num.Length && ra[c] < ra[num[i]] ? -ra[c] : ra[c]).Sum(); static string W(int k, int l = 1) => w.Substring(k, l); static string R(char m, int k) => m == '9' ? W(k-2)+W(k) : m == '5' ? W(k-1) : m == '4' ? W(k-2, 2) : W(k-2); public static string ToRoman(int num) => num < 1 ? "" : (from z in "000100101".Split('1') from m in "9541" select m + z) .Where(z => num >= (o = int.Parse(z))) .Select(z => R(z[0], z.Length * 2)).First() + ToRoman(num - o); } 

These examples are far from optimal in terms of speed, but they can be used. And above all, in learning the basics of LINQ.

Source: https://habr.com/ru/post/271035/


All Articles