【问题标题】:Split list at marker elements with LINQ?使用LINQ在标记元素处拆分列表?
【发布时间】:2010-09-22 19:27:47
【问题描述】:

我有一个List<T>,它混合了“常规”元素和“标记”元素。我想将其拆分为List<List<T>>,在标记处断开。

例如给定 {a, b, M, c, d, e, f, M, g} 的输入,其中 'M' 是标记元素,我想得到 {{a, b},{c, d, e , f}, {g}}

用循环来完成这项工作很容易,但似乎应该可以用 LINQ 更紧凑地表达它,经过一番摸索后,我就是不知道该怎么做。 (我猜是不够 LINQ-fu。)

【问题讨论】:

    标签: .net linq list


    【解决方案1】:

    嗯……

    varSplitList = myList.Aggregate(new List<List<T>>{new List<T>()}, 
       (sl,t)=> {
          if(/*T is a marker element*/) sl.Add(new List<T>());
          else sl.Last().Add(t); 
          return sl;
       });
    

    可能有一种更易读的方式,但这应该可行。聚合是一种非常强大的“系列计算”方法,对这类事情很有用。您为其提供一个“种子”(在本例中为具有单个子列表的新列表列表),并对源中的每个元素执行操作,该操作采用种子和当前元素并返回(可能已修改)种子,然后将其传递给下一个元素的操作。

    作为一个更简单的例子,这是 Linq 中的一个阶乘计算器:

    long fact = Enumerable.Range(1,n).Aggregate(1, (f,n)=&gt;f*n));

    Enumerable.Range() 产生一个从 1 到 n 的整数范围。 Aggregate 函数从 1 开始,对于每个元素,将种子乘以元素:

    1*1=1 1*2=2 2*3=6 6*4=24 24*5=120...

    最后,fact 在所有计算完成后被赋予种子值。

    【讨论】:

    • 我会给你一些风格点(和一个赞成票),但我想说这显然并不比等效的循环实现更紧凑——它几乎只是 LINQ 服装中的循环。
    • 非常真实...几乎所有 LINQ 方法。说真的,LINQ 是一个迭代器隐藏库。
    【解决方案2】:

    对于 LINQ 来说似乎不是一份工作。但是如果我要在不写“LINQ 服装中的循环”的情况下编写它,我会这样做:

    var list = new List<char> {'a', 'b', 'M', 'c', 'd', 'e', 'f', 'M', 'g'};
    const char marker = 'M';
    
    var markerIndexes = list.Select((c, i) => new { c, i }).Where(z => z.c == marker).Select(z => z.i);
    var split = from z in list.Select((c, i) => new { c, i })
                where z.c != marker
                group z.c by markerIndexes.Count(mi => z.i > mi) into g
                select g.ToList();
    return split.ToList();
    

    【讨论】:

    • 相当巧妙,但也(自相矛盾?)有点糟糕。当然比普通的循环实现更难理解,而且也不简洁。根据到目前为止的答案,看起来你是对的——“看起来不像是 LINQ 的工作”。不过,我从研究答案中学到了一些东西 - 谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-10
    • 2010-09-29
    • 2014-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多