C# 使用Linq按特殊值分隔列表?

C# 使用Linq按特殊值分隔列表?,c#,linq,C#,Linq,我正在尝试使用Linq将IEnumerable转换为IEnumerable——输入流将由特殊值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,

我正在尝试使用Linq将
IEnumerable
转换为
IEnumerable
——输入流将由特殊值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}}
IEnumerable解析(IEnumerable l)
{
l、 选择(x=>{
.....; //?
返回新列表();
});
}
var l=新列表{0,1,3,5,0,3,4,0,1,4,0};
Parse(l)//返回{1,3,5},{3,4},{1,4}
如何使用Linq而不是命令式循环来实现它?
或者Linq不适合此要求,因为逻辑取决于输入流的顺序?

编辑以修复数字中零的可能性

下面是一个半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));
var l=新列表{0,1,3,5,0,3,4,0,1,4,0};
一串
.Join(“,”l.Select(x=>x==0?”|“:x.ToString())
.Split(新[]{'|'},StringSplitOptions.RemoveEmptyEntries)
.Select(x=>x.Split(新[]{',},StringSplitOptions.RemoveEmptyEntries));

由于性能和其他原因,这可能不比使用循环更可取,但它应该可以工作。

编辑以修复数字中零的可能性

下面是一个半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));
var l=新列表{0,1,3,5,0,3,4,0,1,4,0};
一串
.Join(“,”l.Select(x=>x==0?”|“:x.ToString())
.Split(新[]{'|'},StringSplitOptions.RemoveEmptyEntries)
.Select(x=>x.Split(新[]{',},StringSplitOptions.RemoveEmptyEntries));

由于性能和其他原因,这可能不比使用循环更好,但它应该可以工作。

简单循环是一个不错的选择

备选方案:

  • 可枚举。聚合
    并在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;
});
var result=list.Aggregate(新列表(),
(总和,当前)=>{
如果(当前==0)
添加(新列表());
其他的
sum.Last().Add(当前);
回报金额;
});
注意:这是为给定非常友好的输入(如{0,1,2,0,3,4})工作的方法的唯一示例


甚至可以将聚合设置为不可变的列表,但对于基本的.Net类型来说,这看起来很疯狂。

简单循环将是一个不错的选择

备选方案:

  • 可枚举。聚合
    并在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;
});
var result=list.Aggregate(新列表(),
(总和,当前)=>{
如果(当前==0)
添加(新列表());
其他的
sum.Last().Add(当前);
回报金额;
});
注意:这是为给定非常友好的输入(如{0,1,2,0,3,4})工作的方法的唯一示例


甚至可以将聚合设置为不可变列表,但对于基本的.Net类型,这看起来很疯狂。

这里有一个答案,它惰性地枚举源可枚举列表,但急切地在0之间枚举每个返回列表的内容。它正确地抛出空输入或给定一个不以零开头的列表(尽管允许通过空列表——这实际上是您必须决定的实现细节)。它不会像至少一个其他答案的可能建议那样,在最后返回一个额外的空列表

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;
   }
}
公共静态IEnumerable解析(此IEnumerable源,int splitValue=0){
if(source==null){
抛出新ArgumentNullException(nameof(source));
}
使用(var enumerator=source.GetEnumerator()){
如果(!enumerator.MoveNext()){
返回可枚举的.Empty();
}
if(enumerator.Current!=splitValue){
抛出新ArgumentException(nameof(source),$“源枚举必须以{splitValue}开头。”);
}
返回ParseImpl(枚举数,splitValue);
}
}
私有静态IEnumerable ParseImpl(IEnumerator枚举器,int splitValue){
var list=新列表();
while(枚举数.MoveNext()){
if(枚举数.Current==splitValue){
收益回报表;
列表=新列表();
}
否则{
list.Add(枚举数.Current);
}
}
if(list.Any()){
收益回报表;
}
}

这可以很容易地调整为泛型而不是
int
,只需将
Parse
更改为
Parse
,将
int
更改为
T
,并在任何地方使用
a.Equals(b)
!a、 等于(b)
而不是
a==b
a!=b

这里有一个答案,它懒散地枚举源可枚举项,但急切地在0之间枚举每个返回列表的内容。它正确地抛出空输入或给定一个不以零开头的列表(尽管允许通过空列表——这实际上是您必须决定的实现细节)。它不会像至少一个其他答案的可能建议那样,在最后返回一个额外的空列表

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;
   }
}
公共静态IEnumerable解析(此IEnumerable源,int splitValue=0){
if(source==null){
抛出新ArgumentNullException(nameof(source));
}
使用(var enumerator=source.GetEnumerator()){
如果(!enumerator.MoveNext()){
返回可枚举的.Empty();
}
if(enumerator.Current!=splitValue){
抛出新ArgumentException(nameof(source),$“源枚举必须以{splitValue}开头。”);
}
返回ParseImpl(枚举数,splitValue);
}
}
私有静态IEnumerable ParseImpl(IEnumerator枚举器,int splitValue){
var list=新列表();
while(枚举数.MoveNext()){
if(枚举数.Current==splitValue){
收益回报表;
列表=新列表();
}
否则{
list.Add(枚举数.Current);
}
}
if(list.Any()){
收益回报表;
}
}
这可以很容易地调整为泛型而不是
int
,只需将
Parse
更改为
Parse
,将
int
更改为
T