Search code examples
c#switch-statementsyntactic-sugar

C# Determine Type using Regex with Switch syntactic sugar


I have a string which based on a regex matching is of format_1 or format_2 etc. I use regex named groups to extract clientId variable.

var sampleName = "001-99-26Jul2023";
var sampleNameFormat1 = @"[0-9]{3}-(?<clientId>[0-9]{2})-[0-9]{2}[a-zA-Z]{3}[0-9]{4}";
var sampleNameFormat2 = @"[0-9]{4}-(?<clientId>[0-9]{2})-[0-9]{2}[a-zA-Z]{3}[0-9]{4}";
var sampleNameFormat3 = @"[0-9]{5}-[0-9]{2}[a-zA-Z]{3}[0-9]{4}";

var regexMatch = sampleName switch
{
    var _ when Regex.IsMatch(sampleName, sampleNameFormat1, RegexOptions.IgnoreCase) => Regex.Match(sampleName, sampleNameFormat1, RegexOptions.IgnoreCase),
    var _ when Regex.IsMatch(sampleName, sampleNameFormat2, RegexOptions.IgnoreCase) => Regex.Match(sampleName, sampleNameFormat2, RegexOptions.IgnoreCase),
    var _ when Regex.IsMatch(sampleName, sampleNameFormat3, RegexOptions.IgnoreCase) => Regex.Match(sampleName, sampleNameFormat3, RegexOptions.IgnoreCase),
    _ => null,
};


var clientIdString = regexMatch.Result("${clientId}");

if(!string.IsNullOrEmpty(clientIdString)) // If check, because `sampleNameFormat3` does not have this named grouping <clientId>
{
    int clientId = ParseInteger(clientIdString);
}

Problems:

  1. I do the regex match call twice. Is there a syntax that makes it more clean? Some way to change the switch statement? Only one Regex.Match call.
  2. As you see in Format 3 there is no <clientId>, so the last assignment
    var clientIdString = regexMatch.Result("${clientId}"); will result in clientIdString = "${clientId}".
    So, the if check will enter and error ParseInteger("${clientId}").
    The solution would be to write: var clientIdString = regexMatch.Groups["clientId"].Value; this assigns empty string and if-check does not enter. I don't know if this is the right way, so I wanted to ask you too what you consider.

Solution

  • The switch is overkill, and doesn't really add much in terms of readability or succinctness.

    Just do

    var m = Regex.Match(sampleName, sampleNameFormat1, RegexOptions.IgnoreCase);
    if (!m.Success)
        m = Regex.Match(sampleName, sampleNameFormat2, RegexOptions.IgnoreCase)
    if (!m.Success)
        m = Regex.Match(sampleName, sampleNameFormat3, RegexOptions.IgnoreCase)
    
    if (m.Success)
    {
        // all your other logic
    }
    

    If you add some number of cases, then I suggest looping over the calls as you and @madreflection suggest in the comments. Something like

    Match match = null;
    foreach (var regexFormat in regexFormats)
    {
        match = Regex.Match(sampleName, regexFormat, regexOptions.IgnoreCase);
        
        if (match.Success) break;
    }
    
    if (!match.Success) return null;
    
    // all your other logic
    

    or

    Match match = null;
    foreach (var regexFormat in regexFormats)
    {
        match = Regex.Match(sampleName, regexFormat, regexOptions.IgnoreCase);
        
        if (match.Success)
        {
            // all your other logic
            return something...;
        }
    }
    
    return null;