假设我有一个看起来像这样的简单结构:
public class Range
{
public DateTime Start { get; set; }
public DateTime End { get; set; }
public Range(DateTime start, DateTime end)
{
this.Start = start;
this.End = end;
}
}
然后我像这样创建一个集合:
var dr1 = new Range(new DateTime(2011, 11, 1, 12, 0, 0),
new DateTime(2011, 11, 1, 13, 0, 0));
var dr2 = new Range(new DateTime(2011, 11, 1, 13, 0, 0),
new DateTime(2011, 11, 1, 14, 0, 0));
var dr3 = new Range(new DateTime(2011, 11, 1, 14, 0, 0),
new DateTime(2011, 11, 1, 15, 0, 0));
var dr4 = new Range(new DateTime(2011, 11, 1, 16, 0, 0),
new DateTime(2011, 11, 1, 17, 0, 0));
var ranges = new List<Range>() { dr1, dr2, dr3, dr4 };
我想做的是对连续的范围进行分组 - 即,如果前一个范围的结束值与下一个范围的开始值相同,则它们是连续的。
我们可以假设范围值中没有冲突/重复或重叠。
在发布的示例中,我最终会得到两个组:
2011-11-1 12:00:00 - 2011-11-1 15:00:00
2011-11-1 16:00:00 - 2011-11-1 17:00:00
为此想出一个迭代解决方案相当容易。但是,是否有一些 LINQ 魔法可以让我在一个漂亮的单行代码中获得它?
最佳答案
你最好的选择是使用 yield
和扩展方法:
static IEnumerable<Range> GroupContinuous(this IEnumerable<Range> ranges)
{
// Validate parameters.
// Can order by start date, no overlaps, no collisions
ranges = ranges.OrderBy(r => r.Start);
// Get the enumerator.
using (IEnumerator<Range> enumerator = ranges.GetEnumerator();
{
// Move to the first item, if nothing, break.
if (!enumerator.MoveNext()) yield break;
// Set the previous range.
Range previous = enumerator.Current;
// Cycle while there are more items.
while (enumerator.MoveNext())
{
// Get the current item.
Range current = enumerator.Current;
// If the start date is equal to the end date
// then merge with the previous and continue.
if (current.Start == previous.End)
{
// Merge.
previous = new Range(previous.Start, current.End);
// Continue.
continue;
}
// Yield the previous item.
yield return previous;
// The previous item is the current item.
previous = current;
}
// Yield the previous item.
yield return previous;
}
}
授予,调用 OrderBy
将导致 ranges
序列的完整迭代,但无法避免。一旦您订购了它,您就可以避免在返回之前必须实现您的结果;如果条件要求,您只需 yield
结果即可。
但是,如果您知道序列是有序的,那么您根本不必调用 OrderBy
,并且可以在遍历列表时yield
项目并在不同的折叠 Range
实例上中断。
最终,如果序列是无序的,那么您有两个选择:
- 排序列表然后处理(记住,
OrderBy
也是延迟的,但必须使用一个完整的迭代来排序序列),使用yield
返回当你有一个项目要处理的时候 - 一次处理整个序列并作为一个完整的物化序列返回
关于c# - LINQ GroupBy 连续时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8100076/