将IList <DateTime>中的连续日期组合到范围中

Rob*_*nik 9 c# linq

  1. 我有一系列带有fromto日期的对象.
  2. 使用类似的东西:

    IList<DateTime> dates =
        this.DateRanges
            .SelectMany(r => new [] { r.From, r.To })
            .Distinct()
            .OrderBy(d => d)
            .ToList();
    
    Run Code Online (Sandbox Code Playgroud)

    我可以获得所有日期,而不会重复任何日期.范围可以完全重叠,部分重叠(上部或下部重叠),触摸或它们可以根本不重叠.

  3. 现在我需要将此列表转换为另一个列表,以便每个连续日期对DateTime在对中间形成一个新生成的实例

    D1      D2      D3              D4  D5
        G1      G2          G3        G4
    
    Run Code Online (Sandbox Code Playgroud)

    其中D n是我与列表中不同的日期,而G m日期是我想在其中间生成的日期.

如何将各个日期的有序列表转换为对,以便我得到如下例所示的对?我想用LINQ代替for循环来形成这些,这可以完成同样的事情.由于延迟表达式树执行,使用LINQ可能会产生更高效的代码.

使用实际示例的其他说明

假设这是我的范围的例子:

D1             D2     D3     D4   D5     D6          D11    D12
|--------------|      |------|    |------|           |------|
       D7                         D8
       |--------------------------|
D9                                              D10
|-----------------------------------------------|
Run Code Online (Sandbox Code Playgroud)

获取不同日期的第一步将导致这些日期:

D1     D7      D2     D3     D4   D5     D6     D10  D11    D12
Run Code Online (Sandbox Code Playgroud)

D9和D8会掉线,因为它们是重复的.

下一步是形成对(我不知道如何使用LINQ执行此操作):

D1-D7, D7-D2, D2-D3, D3-D4, D4-D5, D5-D6, D6-D10, (D10-D11), D11-D12
Run Code Online (Sandbox Code Playgroud)

最后一步必须使用以下方法计算每对的日期:

D new = D from +(D to - D from)/ 2

空范围问题

应优选省略范围D 10 -D 11.但是如果省略它会导致代码过于复杂,那么可以通过单独的检查来保留和排除代码.但如果最初可以将其排除在外那么应该做什么.因此,如果您还提供有关如何形成排除空范围的对的信息,也欢迎您添加该信息.

Bro*_*ass 5

你可以使用Zip():

var middleDates = dates.Zip(dates.Skip(1), 
                            (a, b) => (a.AddTicks((b - a).Ticks / 2)))
                       .ToList();
Run Code Online (Sandbox Code Playgroud)


Rob*_*nik 3

最终解决方案

基于 @DavidB 的想法和 @AakashM 的原始答案的有趣想法,我提出了自己的解决方案,从一组日期中提取范围(同时也省略空范围)并计算范围中间日期。

如果您对此解决方案有任何改进建议或意见,热烈欢迎您发表评论。无论如何,这是我现在使用的最终代码(内联注释解释了其功能):

// counts range overlaps
int counter = 0;

// saves previous date to calculate midrange date
DateTime left = DateTime.Now;

// get mid range dates
IList<DateTime> dates = this.DateRanges

    // select range starts and ends
    .SelectMany(r => new[] {
        new {
            Date = r.From,
            Counter = 1
        },
        new {
            Date = r.To,
            Counter = -1
        }
    })

    // order dates because they come out mixed
    .OrderBy(o => o.Date)

    // convert dates to ranges; when non-empty & non-zero wide get mid date
    .Select(o => {

        // calculate middle date if range isn't empty and not zero wide
        DateTime? result = null;
        if ((counter != 0) && (left != o.Date))
        {
            result = o.Date.AddTicks(new DateTime((o.Date.Ticks - left.Ticks) / 2).Ticks);
        }

        // prepare for next date range
        left = o.Date;
        counter += o.Counter;

        // return middle date when applicable otherwise null
        return result;
    })

    // exclude empty and zero width ranges
    .Where(d => d.HasValue)

    // collect non nullable dates
    .Select(d => d.Value)
    .ToList();
Run Code Online (Sandbox Code Playgroud)