我正努力提高我的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...
}
现在看来,list
是IEnumerable<string[]>
。我一直在查看各种LINQ方法(Join()
,Aggregate()
等),但没有找到任何返回IEnumerable<string>
的方法。我开始认为我需要为此编写一个扩展方法,但是希望看看是否有一个替代方案我不知道。
修改
我最终的目标是结束IEnumerable<int>
,但我认为在此之前我必须在IEnumerable<string>
停下来。如果这可以合并为一步,那就更好了。
答案 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)
这个任务对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();