你能帮助我解决两个范围周期之间存在差异的算法。
例如:
第一组范围期: {[01.01.2015 - 10.01.2015],[2015年1月15日 - 2015年1月30日]}
第二组范围期: {[02.01.2015-20.01.2015],[2015年1月25日 - 2015年1月25日]}
结果应为: {[01.01.2015 - 01.01.2015],[2015年1月21日 - 2015年1月24日],[2015年1月26日 - 2015年1月30日]}
到目前为止,这是我的代码:
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;
}
答案 0 :(得分:3)
就个人而言,我采用延迟初始化方法来做到这一点。
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}");
获得这些结果
1/1/2015 12:00:00 AM至1/1/2015 12:00:00 AM
1/21/2015 12:00:00 AM至1/24/2015 12:00:00 AM
1/26/2015 12:00:00 AM至1/30/2015 12:00:00 AM