破译连续日期范围的逻辑

Logic to decipher consecutive date ranges

假设我有一个 class 插槽如下

public class Slots
{
   //Other properties
    .
    .
    .
    public DateTime StartTime{get;set;}
    public DateTime EndTime{ge;set;}
    //Methods
}

而且我有List<Slots>,确定连续插槽组的最有效方法是什么? 连续时段定义为相对于前一个时段从第二天开始的任何一组时段,没有重叠。 如果有一天的间隔(列表中那天没有时段),那么它应该被认为是另一个时段组的开始。

public List<List<Slots>> GetSlotGroups(List<SLots> slots)
{
    //return  list of slot groups according to logic described above
}

这是我想出的代码。我不知道它是否是最有效的,但它具有可读性和相当快的速度。

    public static List<List<Slots>> GetGroups(List<Slots> slots)
    {
        List<List<Slots>> groups = new List<List<Slots>>();
        DateTime? nextDate = null;
        List<Slots> currentGroup = null;

        foreach (var slot in slots.OrderBy(x => x.StartDate))
        {
            //first time through nextDate and currentGroup are null
            //this condition matches the first time through or any time there is a gap in dates
            if (nextDate == null || nextDate.Value < slot.StartDate)
            {
                if (currentGroup != null)
                {
                    //if currentGroups isn't null then we have a completed group
                    groups.Add(currentGroup);
                }
                //start a new group
                currentGroup = new List<Slots>();
            }
            nextDate = slot.EndDate.AddDays(1);
            currentGroup.Add(slot);
        }

        //if there are no items in the collection currentGroup will still be null, otherwise currentGroup has the last group in it still. We finished iterating before finding a gap in dates
        if (currentGroup != null)
        {
            groups.Add(currentGroup);
        }

        return groups;
    }

此代码通过将前一个时段的结束日期加一来跟踪范围内的下一个日期。当我们从一个槽到另一个槽时,我们附加到一个名为 currentGroup 的临时列表。当我们的下一个日期小于当前时段的开始日期时,我们将当前组添加到名为组的结果列表中,并为当前组创建一个新列表。最后我们大概在 currentGroup 中有一些插槽用于最后一组,所以我们也必须添加那个。

这种操作最好用GetEnumerator来实现。 以下代码要求对列表进行排序。

IEnumerable<IEnumerable<Slot>> ToGroups(IEnumerable<Slot> slots)
{
    using (var ie = slots.GetEnumerator())
    {
        var range = new List<Slot>();
        while (ie.MoveNext())
        {
            if (range.Count > 0)
            {
                if (ie.Current.Start > range[range.Count - 1].End)
                {
                    yield return range;
                    range = new List<Slot>{ie.Current};
                    continue;
                }
            }
            range.Add(ie.Current);
        }
        yield return range;
    }
}

很简单。按 StartTime 排序,然后遍历排序后的集合,如果当前项与前一项不连续,则添加一个新组并使其成为当前项。然后只需将该项目添加到当前组即可。

public static List<List<Slots>> GetSlotGroups(List<Slots> slots)
{
    var slotGroups = new List<List<Slots>>();
    using (var e = slots.OrderBy(slot => slot.StartTime).GetEnumerator())
    {
        List<Slots> currentGroup = null;
        Slots lastSlot = null;
        while (e.MoveNext())
        {
            var currentSlot = e.Current;
            if (lastSlot == null || currentSlot.StartTime.Date.Subtract(lastSlot.EndTime.Date).Days > 1)
                slotGroups.Add(currentGroup = new List<Slots>());
            currentGroup.Add(currentSlot);
            lastSlot = currentSlot;
        }
    }
    return slotGroups;
}

最佳答案基于 George 的代码和 Roberts 的评论,但经过修改以考虑在给定的一天有多个时段。

 protected IEnumerable<IEnumerable<ExamCalendar>>ToBlocks(IEnumerable<ExamCalendar> slots)
    {
        using (var ie = slots.OrderBy(slot => slot.StartDate).GetEnumerator())
        {
            var block = new List<ExamCalendar>();
            while (ie.MoveNext())
            {
                if (block.Count > 0)
                {
                    if (ie.Current.StartDate.Date != block[block.Count - 1].StartDate.Date && ie.Current.StartDate.Date != block[block.Count - 1].EndDate.AddDays(1).Date)
                    {
                        yield return block;
                        block = new List<ExamCalendar> { ie.Current };
                        continue;
                    }
                }
                block.Add(ie.Current);
            }
            yield return block;
        }
    }