Alternative to if, else if

后端 未结 8 1061
后悔当初
后悔当初 2021-02-07 11:20

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 cas

相关标签:
8条回答
  • 2021-02-07 11:48

    One more using LINQ and Dictionary

    var mapping = new Dictionary<string, string>()
                            {
                                { "SWGAS.COM", "Southwest Gas" },
                                { "georgiapower.com", "Georgia Power" }
                                .
                                .
                            };
    
    return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
                  .Select(pair => pair.Value)
                  .FirstOrDefault();
    

    If we prefer empty string instead of null when no key matches we can use the ?? operator:

    return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
                  .Select(pair => pair.Value)
                  .FirstOrDefault() ?? "";
    

    If we should consider the dictionary contains similar strings we add an order by, alphabetically, shortest key will be first, this will pick 'SCE' before 'SCEC'

    return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
                  .OrderBy(pair => pair.Key)
                  .Select(pair => pair.Value)
                  .FirstOrDefault() ?? "";
    
    0 讨论(0)
  • 2021-02-07 11:49

    To avoid the blatant Schlemiel the Painter's approach that looping over all the keys would involve: let's use regular expressions!

    // a dictionary that holds which bill text keyword maps to which provider
    static Dictionary<string, string> BillTextToProvider = new Dictionary<string, string> {
        {"SWGAS.COM", "Southwest Gas"},
        {"georgiapower.com", "Georgia Power"}
        // ...
    };
    
    // a regex that will match any of the keys of this dictionary
    // i.e. any of the bill text keywords
    static Regex BillTextRegex = new Regex(
        string.Join("|", // to alternate between the keywords
                    from key in BillTextToProvider.Keys // grab the keywords
                    select Regex.Escape(key))); // escape any special characters in them
    
    /// If any of the bill text keywords is found, return the corresponding provider.
    /// Otherwise, return null.
    string GetProvider(string billText) 
    {
        var match = BillTextRegex.Match(billText);
        if (match.Success) 
            // the Value of the match will be the found substring
            return BillTextToProvider[match.Value];
        else return null;
    }
    
    // Your original code now reduces to:
    
    var provider = GetProvider(txtvar.BillText);
    // the if is be unnecessary if txtvar.Provider should be null in case it can't be 
    // determined
    if (provider != null) 
        txtvar.Provider = provider;
    

    Making this case-insensitive is a trivial exercise for the reader.

    All that said, this does not even pretend to impose an order on which keywords to look for first - it will find the match that's located earliest in the string. (And then the one that occurs first in the RE.) You do however mention that you're searching through largeish texts; if .NET's RE implementation is at all good this should perform considerably better than 200 naive string searches. (By only making one pass through the string, and maybe a little by merging common prefixes in the compiled RE.)

    If ordering is important to you, you might want to consider looking for an implementation of a better string search algorithm than .NET uses. (Like a variant of Boyer-Moore.)

    0 讨论(0)
  • 2021-02-07 11:51

    What you want is a Dictionary:

    Dictionary<string, string> mapping = new Dictionary<string, string>();
    mapping["SWGAS.COM"] = "Southwest Gas";
    mapping["foo"] = "bar";
    ... as many as you need, maybe read from a file ...
    

    Then just:

    return mapping[inputString];
    

    Done.

    0 讨论(0)
  • 2021-02-07 11:52

    you can use dictionary.

    Dictionary<string, string> textValue = new Dictionary<string, string>();
    foreach (KeyValuePair<string, string> textKey in textValue)
    {
      if(txtvar.BillText.IndexOf(textKey.Key) > -1) 
       return textKey.Value;
    
    }
    
    0 讨论(0)
  • 2021-02-07 12:01

    Since you seem to need to search for the key before returning the value a Dictionary is the right way to go, but you will need to loop over it.

    // dictionary to hold mappings
    Dictionary<string, string> mapping = new Dictionary<string, string>();
    // add your mappings here
    // loop over the keys
    foreach (KeyValuePair<string, string> item in mapping)
    {
        // return value if key found
        if(txtvar.BillText.IndexOf(item.Key) > -1) {
            return item.Value;
        }
    }
    

    EDIT: If you wish to have control over the order in which elemnts are evaluated, use an OrderedDictionary and add the elements in the order in which you want them evaluated.

    0 讨论(0)
  • 2021-02-07 12:03

    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();
    
    0 讨论(0)
提交回复
热议问题