这是我的普遍问题的延续: Linq (GroupBy and Sum) over List<List<string>>
我有这样的查询:
var content = new List<List<string>>
{
new List<string>{ "book", "code", "columnToSum" },
new List<string>{ "abc", "1", "10" },
new List<string>{ "abc", "1", "5" },
new List<string>{ "cde", "1", "6" },
};
var headers = content.First();
var result = content.Skip(1)
.GroupBy(s => new { Code = s[headers.IndexOf("code")], Book = s[headers.IndexOf("book")]})
.Select(g => new
{
Book = g.Key.Book,
Code = g.Key.Code,
Total = g.Select(s => int.Parse(s[headers.IndexOf("columnToSum")])).Sum()
});
这很好但我只是想知道如何处理columnToSum为空的情况?因此,例如,这给出了错误“输入字符串格式不正确”,因为int.Parse失败
var content = new List<List<string>>
{
new List<string>{ "book", "code", "columnToSum" },
new List<string>{ "abc", "1", "10" },
new List<string>{ "abc", "1", "" },
new List<string>{ "cde", "1", "6" },
};
如何优雅地处理这种情况?
答案 0 :(得分:1)
为什么不在字符串的前面添加零?
s => int.Parse("0" + s[headers.IndexOf("columnToSum")])
当然,这是一个大黑客。但是如果您真正担心的唯一例外情况是空字符串,那么它会很快(并且相当)可以解决您的问题。
我想知道你从哪里得到这些空字符串。如果它是像SQL查询一样可以控制的东西,那么为什么不只是将查询更改为“0”而没有值? (只要在代码中的其他地方没有使用空列的意义。)
答案 1 :(得分:0)
该代码假定空字符串为0:
Total = g.Where(s => !String.IsNullOrEmpty(s)).Select(s => int.Parse(s[headers.IndexOf("columnToSum")])).Sum()
答案 2 :(得分:0)
一个选项,使用string.All(Char.IsDigit)
作为预检:
Total = g.Select(s => !string.IsNullOrEmpty(s[headers.IndexOf("columnToSum")]) &&
s[headers.IndexOf("columnToSum")].All(Char.IsDigit) ?
int.Parse(s[headers.IndexOf("columnToSum")]) : 0).Sum())
另一种方法是使用int.TryParse
:
int val = 0;
// ...
Total = g.Select(s => int.TryParse(s[headers.IndexOf("columnToSum")], out val) ?
int.Parse(s[headers.IndexOf("columnToSum")]) : 0).Sum())
答案 3 :(得分:0)
不幸的是,这看起来不太好......
g.Select(s => !s[headers.IndexOf("columnToSum")].Any(Char.IsDigit) ?
0 : Int32.Parse(s[headers.IndexOf("columnToSum")])).Sum()
但是,您可以将其包装在一个很好的扩展方法
中public static class StrExt
{
public static int IntOrDefault(this string str, int defaultValue = 0)
{
return String.IsNullOrEmpty(str) || !str.Any(Char.IsDigit) ? defaultValue : Int32.Parse(str);
}
}
...
g.Select(s => s[headers.IndexOf("columnToSum")].IntOrDefault()).Sum();
如果str
不是数字,则扩展方法可让您灵活地设置所需的默认值 - 如果省略该参数,则默认为0
。
答案 4 :(得分:0)
在这里使用列表是有问题的,我会将其解析为适当的数据结构(如Book类),我认为这会清理代码。如果您正在解析CSV文件,请查看FileHelpers,它是这些类型任务的绝佳库,它可以为您解析为数据结构。
话虽如此,如果你仍然想继续使用这个范例,我认为你可以通过创建两个自定义方法来获得相当干净的代码:一个用于处理标题(我使用的几个地方之一)动态类型,以消除代码中的丑陋字符串)和一个用于解析整数。然后你得到这样的东西:
var headers = GetHeaders(content.First());
var result = from entry in content.Skip(1)
group entry by new {Code = entry[headers.code], Book = entry[headers.book] } into grp
select new {
Book = grp.Key.Book,
Code = grp.Key.Code,
Total = grp.Sum(x => ParseInt(x[headers.columnToSum]))
};
public dynamic GetHeaders(List<string> headersList){
IDictionary<string, object> headers = new ExpandoObject();
for (int i = 0; i < headersList.Count; i++)
headers[headersList[i]] = i;
return headers;
}
public int ParseInt(string s){
int i;
if (int.TryParse(s, out i))
return i;
return 0;
}
答案 5 :(得分:0)
您可以在lambda表达式中使用多行,并在结尾处返回一个值。 所以,而不是
Total = g.Select(s => int.Parse(s[headers.IndexOf("columnToSum")])).Sum()
我会写
Total = g.Select(s => {
int tempInt = 0;
int.TryParse(s[headers.IndexOf("columnToSum")], out tempInt);
return tempInt;
}).Sum()
答案 6 :(得分:-1)
t = new List<List<string>>
{
new List<string>{ "book", "code", "columnToSum" },
new List<string>{ "abc", "1", "10" },
new List<string>{ "abc", "1", "5" },
new List<string>{ "cde", "1", "6" },
};
var headers = content.First();
var result = content.Skip(1)
.GroupBy(s => new { Code = s[headers.IndexOf("code")], Book = s[headers.IndexOf("book")]})
.Select(g => new
{
Book = g.Key.Book,
Code = g.Key.Code,
Total = g.Select(s => int.Parse(s[headers.IndexOf("columnToSum")]!=""?s[headers.IndexOf("columnToSum")]:0)).Sum()
});