如何组合IEnumerable <string []> </string []>

时间:2013-12-12 13:53:42

标签: c# linq

我正努力提高我的LINQ技能,同时从Code Kata开始。现在,我正试图通过使用LINQ而不是foreach循环来改进我在this Kata上的工作。

要求:

有一个方法可以将新行('\n')上的字符串拆分为单独的字符串,然后将每个逗号分隔为最多两个数字。

这是我到目前为止所拥有的:

private int Add(string numbers) {
    var arrays = numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
    var list = arrays.Select(s => s.Split(new char[] { ',', }, StringSplitOptions.RemoveEmptyEntries));

    // Additional code here...
}

现在看来,listIEnumerable<string[]>。我一直在查看各种LINQ方法(Join()Aggregate()等),但没有找到任何返回IEnumerable<string>的方法。我开始认为我需要为此编写一个扩展方法,但是希望看看是否有一个替代方案我不知道。

修改

我最终的目标是结束IEnumerable<int>,但我认为在此之前我必须在IEnumerable<string>停下来。如果这可以合并为一步,那就更好了。

4 个答案:

答案 0 :(得分:7)

幸运的是,它非常简单 - 您只需要SelectMany,它可以作为“扁平”操作:

IEnumerable<string> strings = list.SelectMany(x => x);

或者首先避免使用Select,然后直接转到SelectMany

IEnumerable<string> list = arrays.SelectMany(s => s.Split(new char[] { ',', }, 
                                         StringSplitOptions.RemoveEmptyEntries));

(显然你可以使用var这两个 - 为了清晰起见,我只包括了类型。)

正如Habib所提到的,你甚至不需要中间arrays变量:

IEnumerable<string> list =
     numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
            .SelectMany(s => s.Split(new char[] { ',' }, 
                                     StringSplitOptions.RemoveEmptyEntries));

获得IEnumerable<int>

IEnumerable<int> list =
     numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
            .SelectMany(s => s.Split(new char[] { ',' }, 
                                     StringSplitOptions.RemoveEmptyEntries))
            .Select(s => int.Parse(s));

如果无法将任何字符串解析为整数,这将抛出异常(懒惰)。

答案 1 :(得分:2)

您需要SelectMany,您可以在以下语句中执行此操作:

IEnumerable<string> result = 
    numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
           .Select(r=> r.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
           .SelectMany(r=>r);

如果您需要解析int个值,那么您可以对每个字符串值使用int.TryParse,如:

string numbers = "1,2,3\n4,5,6\n7,8,9";
int temp;
IEnumerable<int> parsedNumbers =
                numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
                .Select(r => r.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                .SelectMany(r => r)
                .Select(r => { return int.TryParse(r, out temp) ? temp : int.MinValue; });
对于无效值,

int.TryParse会失败,在特定情况下,您可以返回任何表示错误的值,例如int.MinValue

编辑:

你也可以这样做:

string numbers = "1,2,3\n4,5,6\n7,8,9";
IEnumerable<int> parsedNumbers =
    numbers.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries)
           .SelectMany(r => r.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
           .Select(int.Parse);

如果出现任何无效值,将抛出异常。

答案 2 :(得分:1)

而不是使用arrays.Select()使用arrays.SelectMany(),它应该返回一个字符串数组,而不是字符串数组的枚举。

答案 3 :(得分:0)

Yaay LINQ时间!我刚刚重新阅读Jon Skeet Edulinq SelectMany部分。

这个任务对LINQ来说不是很好,因为你需要控制代码并且你有特定的情况。无论哪种方式,我会使用某种更好的标记器,更安全,更好的错误处理。这不是为单行启动LINQ的情况。

Ps,你可以使用SelectMany语句逃脱。

int sum = numbers
    .Split(new[] {"\r\n", "\n"}, StringSplitOptions.None)
    .SelectMany(line => line.Split(new[] {','}), 
                   (line, number) => int.Parse(number))
    .Sum();