Alternative to if, else if

Milne picture Milne · Sep 12, 2013 · Viewed 18.7k times · Source

I have a lot of if, else if statements and I know there has to be a better way to do this but even after searching stackoverflow I'm unsure of how to do so in my particular case.

I am parsing text files (bills) and assigning the name of the service provider to a variable (txtvar.Provider) based on if certain strings appear on the bill.

This is a small sample of what I'm doing (don't laugh, I know it's messy). All in all, There are approximately 300 if, else if's.

if (txtvar.BillText.IndexOf("SWGAS.COM") > -1)
{
    txtvar.Provider = "Southwest Gas";
}
else if (txtvar.BillText.IndexOf("georgiapower.com") > -1)
{
    txtvar.Provider = "Georgia Power";
}
else if (txtvar.BillText.IndexOf("City of Austin") > -1)
{
    txtvar.Provider = "City of Austin";
}
// And so forth for many different strings

I would like to use something like a switch statement to be more efficient and readable but I'm unsure of how I would compare the BillText. I'm looking for something like this but can't figure out how to make it work.

switch (txtvar.BillText)
{
    case txtvar.BillText.IndexOf("Southwest Gas") > -1:
        txtvar.Provider = "Southwest Gas";
        break;
    case txtvar.BillText.IndexOf("TexasGas.com") > -1:
        txtvar.Provider = "Texas Gas";
        break;
    case txtvar.BillText.IndexOf("Southern") > -1:
        txtvar.Provider = "Southern Power & Gas";
        break;
}

I'm definitely open to ideas.

I would need the ability to determine the order in which the values were evaluated. As you can imagine, when parsing for hundreds of slightly different layouts I occasionally run into the issue of not having a distinctly unique indicator as to what service provider the bill belongs to.

Answer

Joshua Honig picture Joshua Honig · Sep 12, 2013

Why not use everything C# has to offer? The following use of anonymous types, collection initializers, implicitly typed variables, and lambda-syntax LINQ is compact, intuitive, and maintains your modified requirement that patterns be evaluated in order:

var providerMap = new[] {
    new { Pattern = "SWGAS.COM"       , Name = "Southwest Gas" },
    new { Pattern = "georgiapower.com", Name = "Georgia Power" },
    // More specific first
    new { Pattern = "City of Austin"  , Name = "City of Austin" },   
    // Then more general
    new { Pattern = "Austin"          , Name = "Austin Electric Company" }   
    // And for everything else:
    new { Pattern = String.Empty      , Name = "Unknown" }
};

txtVar.Provider = providerMap.First(p => txtVar.BillText.IndexOf(p.Pattern) > -1).Name; 

More likely, the pairs of patterns would come from a configurable source, such as:

var providerMap =
    System.IO.File.ReadLines(@"C:\some\folder\providers.psv")
    .Select(line => line.Split('|'))
    .Select(parts => new { Pattern = parts[0], Name = parts[1] }).ToList();

Finally, as @millimoose points out, anonymous types are less useful when passed between methods. In that case we can define a trival Provider class and use object initializers for nearly identical syntax:

class Provider { 
    public string Pattern { get; set; } 
    public string Name { get; set; } 
}

var providerMap =
    System.IO.File.ReadLines(@"C:\some\folder\providers.psv")
    .Select(line => line.Split('|'))
    .Select(parts => new Provider() { Pattern = parts[0], Name = parts[1] }).ToList();