替代if,否则if

Mil*_*lne 15 c# performance dictionary if-statement loop-unrolling

我有很多if,else if语句,我知道必须有更好的方法来做到这一点,但即使在搜索stackoverflow之后,我也不确定如何在我的特定情况下这样做.

我正在解析文本文件(账单)并根据账单上是否出现某些字符串将服务提供商的名称分配给变量(txtvar.Provider).

这是我正在做的一小部分样本(不要笑,我知道它很乱).总而言之,如果是,那么大约有300个.

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
Run Code Online (Sandbox Code Playgroud)

我想使用类似switch语句的东西更高效和可读,但我不确定如何比较BillText.我正在寻找这样的东西,但无法弄清楚如何使它工作.

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;
}
Run Code Online (Sandbox Code Playgroud)

我绝对乐观.

编辑:回答正在考虑的问题......是的,我需要能够确定评估值的顺序.可以想象,在解析数百种略有不同的布局时,我偶尔会遇到这样一个问题,即该帐单所属的服务提供商没有明确的唯一指标.(感谢所有令人敬畏的建议!我已经离开办公室几天了,并会尽快解决它们)

Jos*_*nig 22

为什么不使用C#提供的所有东西?以下对匿名类型,集合初始值设定项,隐式类型变量和lambda语法LINQ的使用是紧凑,直观的,并维护您修改的要求,即按顺序评估模式:

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; 
Run Code Online (Sandbox Code Playgroud)

更可能的是,模式对将来自可配置的源,例如:

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();
Run Code Online (Sandbox Code Playgroud)

最后,正如@millimoose指出的那样,在方法之间传递时,匿名类型不太有用.在这种情况下,我们可以定义一个trival Provider类并使用对象初始化器来获得几乎相同的语法:

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();
Run Code Online (Sandbox Code Playgroud)


Ser*_*lis 15

由于您似乎需要在返回值之前搜索密钥,因此a Dictionary是正确的方法,但您需要循环它.

// 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:如果您希望控制评估OrderedDictionary元素的顺序,请使用a 并按照您希望它们评估的顺序添加元素.

  • 字典的缺点是您无法控制评估的顺序.您可能会遇到"SCE"转到"Southern California Edison"并且"SCEC"转到"South Carolina Electric Company"的情况.你想在SCE之前寻找SCEC.最好使用`List <Pair>`结构.您不需要快速查找Dictionary哈希提供的内容. (4认同)
  • @MarkLakata [OrderedDictionary](http://msdn.microsoft.com/en-us/library/system.collections.specialized.ordereddictionary.aspx) (4认同)

Tom*_*nes 10

还有一个使用LINQ和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();
Run Code Online (Sandbox Code Playgroud)

如果我们更喜欢空字符串而不是null,当没有键匹配时,我们可以使用?? 运营商:

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

如果我们应该考虑字典包含类似的字符串,我们按字母顺序添加一个顺序,最短的密钥将是第一个,这将在'SCEC'之前选择'SCE'

return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
              .OrderBy(pair => pair.Key)
              .Select(pair => pair.Value)
              .FirstOrDefault() ?? "";
Run Code Online (Sandbox Code Playgroud)


mil*_*ose 7

为了避免公然的Schlemiel画家的方法,循环所有键将涉及:让我们使用正则表达式!

// 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;
Run Code Online (Sandbox Code Playgroud)

使这种不区分大小写对读者来说是一项微不足道的练习.

总而言之,这甚至不假装首先要对哪些关键字进行查询 - 它会找到最早位于字符串中的匹配.(然后是在RE中首先出现的那个.)但是,你提到你正在搜索大文本; 如果.NET的RE实现完全没有问题,这应该比200个天真的字符串搜索要好得多.(仅通过字符串进行一次传递,并且可能通过合并编译的RE中的公共前缀来进行一些传递.)

如果排序对您很重要,您可能需要考虑寻找比.NET使用更好的字符串搜索算法的实现.(就像Boyer-Moore的变种一样.)

  • 教学历史+1也是如此:) http://en.wikipedia.org/wiki/Schlemiel_the_Painter's_algorithm (2认同)