【问题标题】:c# difference for two list of range periods两个范围周期列表的c#差异
【发布时间】:2016-02-25 16:34:02
【问题描述】:

你能帮我用一个算法来区分两组范围周期吗?

例如:

第一组范围周期: {[01.01.2015 - 10.01.2015] , [15.01.2015 - 30.01.2015]}

第二组区间区间: {[02.01.2015 - 20.01.2015] , [25.01.2015 - 25.01.2015]}

结果应该是: {[01.01.2015 - 01.01.2015],[21.01.2015 - 24.01.2015],[26.01.2015-30.01.2015]}

这是我目前的代码:

        private object GetDifferenceRangePeriods(List<RangePeriod> all, List<RangePeriod> toRemove)
    {
        foreach (var rp in toRemove)
        {
            var overlappedItems = all.Where(c => DateHelper.IsOverlapping(c.StartDate, c.EndDate, rp.StartDate, rp.EndDate)).ToList();
            foreach (var itm in overlappedItems)
            {
                if (itm.StartDate == rp.StartDate)
                {
                    if (itm.EndDate <= rp.EndDate)
                    {
                        all.Remove(itm);
                    }
                    else if(itm.EndDate > rp.EndDate)
                    {
                        itm.StartDate = rp.EndDate.AddDays(+1);
                    }
                }

                if (itm.StartDate < rp.StartDate)
                {
                    if (itm.EndDate <= rp.EndDate)
                    {
                        itm.EndDate = rp.StartDate.AddDays(-1);
                    }
                    if (itm.EndDate > rp.EndDate)
                    {
                        itm.EndDate= rp.StartDate.AddDays(-1);

                        var newRangePeriod = new RangePeriod
                        {
                            StartDate = rp.EndDate.AddDays(+1),
                            EndDate = itm.EndDate
                        };
                        all.Add(newRangePeriod);
                    }
                }

                if (itm.StartDate > rp.StartDate)
                {
                    if (itm.EndDate <= rp.EndDate)
                    {
                        all.Remove(itm);
                    }
                    if (itm.EndDate > rp.EndDate)
                    {
                        itm.StartDate = itm.EndDate.AddDays(+1);
                    }
                }
            }
        }

        return all;
    }

【问题讨论】:

  • 不清楚!你能分享一些代码吗?你尝试了什么?
  • 第一组包括范围,第二组排除范围吗?如果是这样,可能想在问题中说明这一点。
  • @ebyrob 我认为这就是 OP 所说的差异,就像第一组减去第二组一样。
  • 既然您已经共享了一些代码,您能否更具体地说明给您带来问题的确切部分?也许您可以说出当前代码的结果。
  • 为什么你的方法返回object而不是List&lt;RangePeriod&gt;?此外,您真的应该创建一个新范围的新列表,而不是改变传入的 all 列表。

标签: c# algorithm date date-range difference


【解决方案1】:

我个人会使用惰性初始化方法来做到这一点。

public static IEnumerable<RangePeriod> Minus(
    this IEnumerable<RangePeriod> first,
    IEnumerable<RangePeriod> second)
{
    // First make sure that the lists are ordered by their start dates.
    var firstSorted = first.OrderBy(r => r.StartDate);
    var secondSorted = second.OrderBy(r => r.StartDate);

    // get the enumerators of the sorted sequences.
    using (var keep = firstSorted.GetEnumerator())
    using (var remove = secondSorted.GetEnumerator())
    {
        // if there are no ranges to keep then return an empy sequence.
        if (!keep.MoveNext()) yield break;
        var currentKeep = keep.Current;
        RangePeriod currentRemove = null;
        if (remove.MoveNext()) currentRemove = remove.Current;
        while (true)
        {
            // if there are no more remove ranges or the remove range is after the keep
            // then just yield the keep range and move to the next keep range.
            if (currentRemove == null || currentKeep.EndDate < currentRemove.StartDate)
            {
                yield return currentKeep;
                if (!keep.MoveNext()) yield break;
                currentKeep = keep.Current;
                continue;
            }

            // if the remove range is before the keep then move to the next remove range.
            if (currentRemove.EndDate < currentKeep.StartDate)
            {
                currentRemove = remove.MoveNext() ? remove.Current : null;
                continue;
            }

            // if the remove range ends before the keep range
            if (currentRemove.EndDate < currentKeep.EndDate)
            {
                // if the keep starts before the remove then we yield a range from the keep's start
                // to the remove's start - 1 day.
                if (currentKeep.StartDate < currentRemove.StartDate)
                {
                    yield return new RangePeriod(currentKeep.StartDate, currentRemove.StartDate.AddDays(-1));
                }

                // change the keep's start to the remove's end + 1 and move to the next remove range.
                currentKeep = new RangePeriod(currentRemove.EndDate.AddDays(1), currentKeep.EndDate);
                currentRemove = remove.MoveNext() ? remove.Current : null;
                continue;
            }

            // if the remove range completely covers the keep then move to the next keep (if there is one)
            if (currentRemove.StartDate < currentKeep.StartDate)
            {
                if (!keep.MoveNext()) yield break;
                currentKeep = keep.Current;
                continue;
            }

            // Otherwise the remove range starts after the keep starts but before the keep ends and the
            // remove ends after the keep ends, so we need to yield a range that starts on the keep's
            // start and ends before the remove's start and move to the next keep range.
            yield return new RangePeriod(currentKeep.StartDate, currentRemove.StartDate.AddDays(-1));
            if (!keep.MoveNext()) yield break;
            currentKeep = keep.Current;
        }
    }
}

这将允许您执行以下操作

var first = new[]
            {
                new RangePeriod(new DateTime(2015, 1, 1), new DateTime(2015, 1, 10)),
                new RangePeriod(new DateTime(2015, 1, 15), new DateTime(2015, 1, 30))
            };
var second = new[]
            {
                new RangePeriod(new DateTime(2015, 1, 2), new DateTime(2015, 1, 20)),
                new RangePeriod(new DateTime(2015, 1, 25), new DateTime(2015, 1, 25))
            };

foreach(var range in first.Minus(second)) 
    Console.WriteLine($"{range.StartDate} to {range.EndDate}");

并得到这些结果

2015 年 1 月 1 日凌晨 12:00:00 至 2015 年 1 月 1 日凌晨 12:00:00

2015 年 1 月 21 日凌晨 12:00:00 至 2015 年 1 月 24 日凌晨 12:00:00

2015 年 1 月 26 日凌晨 12:00:00 至 2015 年 1 月 30 日凌晨 12:00:00

【讨论】:

    猜你喜欢
    • 2013-04-12
    • 1970-01-01
    • 2014-03-09
    • 2016-05-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-05-08
    • 2014-10-12
    相关资源
    最近更新 更多