Ershad Nozari
Ershad Nozari

Reputation: 667

C# LINQ and Pattern Matching challenge

I need a solution that checks whether the content of a string of fixed lenght adheres to a set of rules. If not, I need to retrieve a list of the Rules that failed, the Expected value for each rule, and the Actually value contained within the string.

This is my current solution:

string actual = "628IDENTREGISTER153004085616P30062010EAPFEMPA013.1";

 // Dictionary<Tuple<rule, expected>, startingPostion>
 var expected = new Dictionary<Tuple<string, string>, int>
 {
   {new Tuple<string, string>("900052", "628"), 0},
   {new Tuple<string, string>("9000250", "IDENTREGISTER1"), 3},
   {new Tuple<string, string>("900092", "53004085616"), 17},
   {new Tuple<string, string>("900004", "P"), 28}, 
   {new Tuple<string, string>("900089", "30062010"), 29},
   {new Tuple<string, string>("900028", "E"), 37},
   {new Tuple<string, string>("900029", "A"), 38},
   {new Tuple<string, string>("900002", "P"), 39},        
   {new Tuple<string, string>("900030", "FEMPA013.0"), 40}  
 };

 // Create an IEnumerable of all broken rules 
 var result = expected.Where(field => 
    !field.Key.Item2.Equals(
       actual.Substring(field.Value, field.Key.Item2.Length)))

 // Prints: 
 // [(900030, FEMPA013.0), 40]
 foreach (var res in result)
   Console.WriteLine(res);

I'm sure there’s a better way of solving this problem. Also, as it stands, I’m not entirely satisfied with this solution as it does not give me the actual field.

Thanks.

Upvotes: 1

Views: 953

Answers (2)

Jonathon Chase
Jonathon Chase

Reputation: 9704

Any reason you couldn't just wrap the rule along with the inspected portion in a Tuple?

If not, I would do something like this:

var result = from field in expected
             let inspected = actual.Substring(field.Value, field.Key.Item2.Length)
             where !field.Key.Item2.Equals(inspected)
             select (field, inspected);

Which would then, given your example above, output:

([(900030, FEMPA013.0), 40], FEMPA013.1)

You could unpack the rule entry a little further in the select as well, something along the lines of select (field.Key.Item1, field.Key.Item2, inspected);, and you'll end up with a tuple of (RuleId, expected, actual)

Upvotes: 1

NetMage
NetMage

Reputation: 26917

You should create a class to represent a rule, and have some helper methods in the class:

public class Rule {
    public string RuleName;
    public string Expected;
    public int StartPos;

    public bool IsMatch(string actual) => Field(actual) == Expected;
    public string Field(string actual) => actual.Substring(StartPos, Math.Min(Expected.Length, actual.Length-StartPos));
public override string ToString() => $"{{ {RuleName}: @{StartPos}=\"{Expected}\" }}";
}

Now you can just need a List<Rule> to hold the rules:

var expected = new List<Rule> {
   new Rule { RuleName = "900052", Expected = "628", StartPos = 0 },
   new Rule { RuleName = "9000250", Expected = "IDENTREGISTER1", StartPos = 3 },
   new Rule { RuleName = "900092", Expected = "53004085616", StartPos = 17 },
   new Rule { RuleName = "900004", Expected = "P", StartPos = 28 },
   new Rule { RuleName = "900089", Expected = "30062010", StartPos = 29 },
   new Rule { RuleName = "900028", Expected = "E", StartPos = 37 },
   new Rule { RuleName = "900029", Expected = "A", StartPos = 38 },
   new Rule { RuleName = "900002", Expected = "P", StartPos = 39 },
   new Rule { RuleName = "900030", Expected = "FEMPA013.0", StartPos = 40 }
 };

And you can find the bad rules and extract the bad fields:

string actual = "628IDENTREGISTER153004085616P30062010EAPFEMPA013.1";

var result = expected.Where(rule => !rule.IsMatch(actual)).Select(rule => new { rule, Actual = rule.Field(actual) });

foreach (var res in result)
    Console.WriteLine(res);

// Output is
// { rule = { 900030: @40="FEMPA013.0" }, Actual = FEMPA013.1 }

Upvotes: 0

Related Questions