我有很多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
我想使用类似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;
}
我绝对乐观。
编辑:要回答正在考虑的问题...是的,我需要能够确定评估值的顺序。 可以想象,在解析数百种略有不同的布局时,我偶尔会遇到这样一个问题,即该帐单所属的服务提供商没有明确的唯一指标。 (感谢所有令人敬畏的建议!我已经离开办公室几天了,并会尽快出去尝试)
答案 0 :(得分:21)
为什么不使用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;
更有可能的是,这些模式对来自可配置的源,例如:
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();
最后,正如@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();
答案 1 :(得分:15)
由于您似乎需要在返回值之前搜索密钥,因此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;
}
}
编辑:如果您希望控制评估元素的顺序,请使用OrderedDictionary
并按照您希望评估它们的顺序添加元素。
答案 2 :(得分: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();
如果没有键匹配我们更喜欢空字符串而不是null我们可以使用??操作者:
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
如果我们应该考虑字典包含类似的字符串,我们按字母顺序添加一个顺序,最短的密钥将是第一个,这将在'SCEC'之前选择'SCE'
return mapping.Where(pair => txtvar.BillText.IndexOf(pair.Key) > -1)
.OrderBy(pair => pair.Key)
.Select(pair => pair.Value)
.FirstOrDefault() ?? "";
答案 3 :(得分:7)
为了避免公然的施莱米尔画家的方法,循环所有的键将涉及:让我们使用正则表达式!
// 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;
使这种不区分大小写对读者来说是一项微不足道的练习。
所有这一切,甚至没有假装首先要对哪些关键字进行查询 - 它会在字符串中找到位于最早的匹配。 (然后是在RE中首先出现的那个。)但是,你提到你正在搜索大文本;如果.NET的RE实现完全没有问题,这应该比200个天真的字符串搜索要好得多。 (通过只对一个字符串进行一次传递,也可以通过在编译的RE中合并公共前缀来进行一些传递。)
如果排序对您很重要,您可能需要考虑寻找比.NET使用更好的字符串搜索算法的实现。 (就像Boyer-Moore的变种一样。)
答案 4 :(得分:4)
你想要的是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 ...
然后只是:
return mapping[inputString];
完成。
答案 5 :(得分:4)
一种方法(其他答案显示非常有效的选项):
void Main()
{
string input = "georgiapower.com";
string output = null;
// an array of string arrays...an array of Tuples would also work,
// or a List<T> with any two-member type, etc.
var search = new []{
new []{ "SWGAS.COM", "Southwest Gas"},
new []{ "georgiapower.com", "Georgia Power"},
new []{ "City of Austin", "City of Austin"}
};
for( int i = 0; i < search.Length; i++ ){
// more complex search logic could go here (e.g. a regex)
if( input.IndexOf( search[i][0] ) > -1 ){
output = search[i][1];
break;
}
}
// (optional) check that a valid result was found.
if( output == null ){
throw new InvalidOperationException( "A match was not found." );
}
// Assign the result, output it, etc.
Console.WriteLine( output );
}
这项练习的主要内容是创建一个巨大的switch
或if/else
结构并不是最好的方法。
答案 6 :(得分:1)
有几种方法可以做到这一点,但为了简单起见,条件运算符可能是一个选择:
Func<String, bool> contains=x => {
return txtvar.BillText.IndexOf(x)>-1;
};
txtvar.Provider=
contains("SWGAS.COM")?"Southwest Gas":
contains("georgiapower.com")?"Georgia Power":
contains("City of Austin")?"City of Austin":
// more statements go here
// if none of these matched, txtvar.Provider is assigned to itself
txtvar.Provider;
请注意,结果是根据更符合条件的条件,因此如果txtvar.BillText="City of Austin georgiapower.com";
,则结果为"Georgia Power"
。
答案 7 :(得分:0)
你可以使用字典。
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;
}