【问题标题】:Logic to decipher consecutive date ranges破译连续日期范围的逻辑
【发布时间】:2015-09-28 19:38:08
【问题描述】:

假设我有一个类 Slots 如下

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
}

【问题讨论】:

  • 你们有重叠期吗?
  • 什么是间隙?能否给出一个示例函数输入和输出?
  • 修改了问题。信息够了吗?

标签: c# linq logic


【解决方案1】:

这种操作最好用 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;
    }
}

【讨论】:

  • 能否请您评论代码以解释逻辑?它是如何处理日差的?
  • 查看唯一比较日期的条件语句
  • 条件应该是if (ie.Current.StartDate.Date!=range[range.Count-1].EndDate.AddDays(1).Date)否则,很好的答案,甚至返回一个IEnumerable!
  • 罗伯特,根据我的经验,最好的做法是保持结束日期时间不包含在内。否则,您实际上需要 DateTime 以外的其他内容,例如 DateWithoutTime 类或整数天数。
【解决方案2】:

这很简单。按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;
}

【讨论】:

  • 这个效率比yield高吗?
  • 如果您只打算foreach 返回集一次,那不是真的。但这不是重点,上面的实现使用最少的属性/索引器访问器以及 DateTime 操作/比较。一切都是有代价的。很多人会说这些是微优化,但是一旦您要求“最有效的方式”... :-) 好处是它不会牺牲可读性(当然是 IMO)。
【解决方案3】:

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

    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 中为最后一个组提供了一些插槽,因此我们也必须添加那个。

【讨论】:

  • 编辑解释多一点。
  • 抱歉误删了我的评论。这不会在我的列表中只给我一个 List 吗?
  • 我认为你错过了嵌套在 foreach 循环中的 groups.Add(currentGroup)。
  • 对不起,我的错。是的,它看起来不错,让我进行一些测试。感谢您为此付出的努力
【解决方案4】:

最佳答案是基于 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;
        }
    }

【讨论】:

  • 原始问题的哪一部分证明这是“最好的”?方法签名不同,不是最有效的实现,并且在传递空 slots 参数时返回不正确的结果。
  • 空了怎么办?据我所知,它将返回 null 或空?
  • 它将返回包含空列表的单个项目。
猜你喜欢
  • 2019-04-24
  • 2014-12-16
  • 2013-03-24
  • 2013-01-15
  • 1970-01-01
  • 2011-08-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多