如何在LINQ中对范围进行分组

时间:2014-03-07 16:26:40

标签: c# linq linq-to-objects

以下是我想要分组的数据。

Start      End
  2         4
  26        30
  5         9
  20        24
  18        19

因为我有18 - 19和20 - 24.我会将这两个加在一起作为18 - 24.在这种情况下,规则是(a,b)=> b.start - a.end = 1,结果将是

Start      End
  18        24
  2         9
  26        30

EDIT在下面的评论中添加了最后一个结果行。

1 个答案:

答案 0 :(得分:5)

因此,我们将从名为GroupWhile的辅助方法开始。它将提供一个谓词,接受序列,前一个和当前的两个项目。如果该谓词返回true,则当前项目与前一项目进入同一组。如果没有,它会启动一个新组。

public static IEnumerable<IEnumerable<T>> GroupWhile<T>(
    this IEnumerable<T> source, Func<T, T, bool> predicate)
{
    using (var iterator = source.GetEnumerator())
    {
        if (!iterator.MoveNext())
            yield break;

        List<T> list = new List<T>() { iterator.Current };

        T previous = iterator.Current;

        while (iterator.MoveNext())
        {
            if (!predicate(previous, iterator.Current))
            {
                yield return list;
                list = new List<T>();
            }

            list.Add(iterator.Current);
            previous = iterator.Current;
        }
        yield return list;
    }
}

一旦我们有了这个,我们可以在开始时按顺序排序项目,然后在结束日期之前对它们进行分组,同时将前一个范围的结束与下一个范围的开始重叠,然后根据组开始将每个组折叠到新的范围内和结束价值。

var collapsedRanges = ranges.OrderBy(range => range.Start)
    .ThenBy(range => range.End)
    .GroupWhile((prev, cur) => prev.End + 1 >= cur.Start)
    .Select(group => new Range()
    {
        Start = group.First().Start,
        End = group.Select(range => range.End).Max(),
    });