我正在尝试使用Linq将IEnumerable<int>
转换为IEnumerable<List<int>>
- 输入流将以特殊值0分隔。
IEnumerable<List<int>> Parse(IEnumerable<int> l)
{
l.Select(x => {
.....; //?
return new List<int>();
});
}
var l = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
Parse(l) // returns {{1,3,5}, {3, 4}, {1,4}}
如何使用Linq而不是命令式循环来实现它? 或者Linq不满足于此要求,因为逻辑取决于输入流的顺序?
答案 0 :(得分:4)
简单循环将是不错的选择。
替代方案:
Enumerable.Aggregate
并在0 汇总样本
var result = list.Aggregate(new List<List<int>>(),
(sum,current) => {
if(current == 0)
sum.Add(new List<int>());
else
sum.Last().Add(current);
return sum;
});
注意:这只是适用于{0,1,2,0,3,4}等非常友好输入的方法示例。
甚至可以将聚合聚合到不可变列表中,但对于基本的.Net类型,这看起来会很疯狂。
答案 1 :(得分:2)
这是一个懒惰地枚举源可枚举的答案,但是在零之间急切地枚举每个返回列表的内容。它正确地抛出空输入或者给出一个不以零开头的列表(尽管允许空列表通过 - 这实际上是一个你必须决定的实现细节)。它最终不会返回额外的空列表,就像至少其他答案的可能建议一样。
public static IEnumerable<List<int>> Parse(this IEnumerable<int> source, int splitValue = 0) {
if (source == null) {
throw new ArgumentNullException(nameof (source));
}
using (var enumerator = source.GetEnumerator()) {
if (!enumerator.MoveNext()) {
return Enumerable.Empty<List<int>>();
}
if (enumerator.Current != splitValue) {
throw new ArgumentException(nameof (source), $"Source enumerable must begin with a {splitValue}.");
}
return ParseImpl(enumerator, splitValue);
}
}
private static IEnumerable<List<int>> ParseImpl(IEnumerator<int> enumerator, int splitValue) {
var list = new List<int>();
while (enumerator.MoveNext()) {
if (enumerator.Current == splitValue) {
yield return list;
list = new List<int>();
}
else {
list.Add(enumerator.Current);
}
}
if (list.Any()) {
yield return list;
}
}
这可以很容易地适用于通用而不是int
,只需将Parse
更改为Parse<T>
,将int
更改为T
,并使用{ {1}}或a.Equals(b)
代替!a.Equals(b)
或a == b
。
答案 2 :(得分:1)
你可以创建一个像这样的扩展方法:
public static IEnumerable<IEnumerable<T>> SplitBy<T>(this IEnumerable<T> source, T value)
{
using (var e = source.GetEnumerator())
{
if (e.MoveNext())
{
var list = new List<T> { };
//In case the source doesn't start with 0
if (!e.Current.Equals(value))
{
list.Add(e.Current);
}
while (e.MoveNext())
{
if ( !e.Current.Equals(value))
{
list.Add(e.Current);
}
else
{
yield return list;
list = new List<T> { };
}
}
//In case the source doesn't end with 0
if (list.Count>0)
{
yield return list;
}
}
}
}
然后,您可以执行以下操作:
var l = new List<int> { 0, 1, 3, 5, 0, 3, 4, 0, 1, 4, 0 };
var result = l.SplitBy(0);
答案 3 :(得分:0)
您可以将GroupBy
与计数器一起使用。
var list = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
int counter = 0;
var result = list.GroupBy(x => x==0 ? counter++ : counter)
.Select(g => g.TakeWhile(x => x!=0).ToList())
.Where(l => l.Any());
答案 4 :(得分:-2)
已编辑以修复数字中零的可能性
这是一个半LINQ解决方案:
var l = new List<int> {0,1,3,5,0,3,4,0,1,4,0};
string
.Join(",", l.Select(x => x == 0 ? "|" : x.ToString()))
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => x.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries));
由于性能和其他原因,这可能不适合使用循环,但它应该可以工作。