Linq:Sum()非整数值

时间:2014-02-17 12:47:01

标签: c# .net linq

这是我的普遍问题的延续: 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" },
        };

如何优雅地处理这种情况?

7 个答案:

答案 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()
             });