Reputation: 602
I am working on an algorithm to calculate a continuous overlap of multiple date ranges. It also needs to have a set number of times that it overlaps. For the example image below, I need 3 dates to overlap continuously. The valid overlapping dates would be Aug 20 - Aug 23, as Aug 24 only has 2 overlapping.
I have attempted many approaches including looping through all dates and comparing each one indivually with the next. That code looks like this.
Here is a .net fiddle for better visualization: https://dotnetfiddle.net/x3LfHR#.
private bool Compare(CompareDate a, CompareDate b)
{
DateTime? tStartA = a.ActiveDate;
DateTime? tEndA = a.ExpireDate;
DateTime? tStartB = b.ActiveDate;
DateTime? tEndB= b.ExpireDate;
bool overlap = (tStartA <= tEndB || tEndB == null) && (tStartB <= tEndA || tEndA == null);
DateTime? overlapStart = null;
DateTime? overlapEnd = null;
if (overlap)
{
//Find maximum start date.
overlapStart = (tStartA >= tStartB) ? tStartA : tStartB;
//Find Min End date between the two
overlapEnd = (tEndA <= tEndB) ? tEndA : tEndB;
if (overlapStart > this.overlapStart || this.overlapStart == null)
{
this.overlapStart = overlapStart;
}
if (overlapEnd < this.overlapEnd || this.overlapEnd == null)
{
this.overlapEnd = overlapEnd;
}
However, this approach makes it tricky to figure out continuous overlap dates. I have attempted to use the .Net Time period library at https://www.codeproject.com/Articles/168662/Time-Period-Library-for-NET, but its not relevant in my case. Any help is appreciated.
Upvotes: 1
Views: 1396
Reputation: 15794
OK - LINQ to the rescue!
Note: In order to make the comparison work, you must remove the time component and strictly use the only the date (e.g. DateTime.Date). Based on your requirements, that's exactly how you need to do it, so it shouldn't be a problem.
public List<DateTime> CompareDates(List<DateTime[]> compareRanges, int overlapLevel = 1)
{
var grouped = compareRanges.SelectMany(r => r).GroupBy(d => d);
var thresholdMatch = grouped.Where(g => g.Count() >= overlapLevel)
.Select(g => g.Key)
.OrderBy(d => d)
.ToList();
return thresholdMatch;
}
You can test the logic in a sample console app, using the skeleton code below as an example:
static void Main()
{
var setA = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
};
var setB = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
};
var setC = new[]
{
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
new DateTime(2017, 8, 24),
new DateTime(2017, 8, 25),
new DateTime(2017, 8, 26),
};
var setD = new[]
{
new DateTime(2017, 8, 20),
new DateTime(2017, 8, 21),
new DateTime(2017, 8, 22),
new DateTime(2017, 8, 23),
};
var compareList = new List<DateTime[]>
{
setA, setB, setC, setD
};
// setting the threshold to 2 will cause 8/24 to be added to the result...
// setting this to 1 (default) will return all intersections
// for now, set it to 3, per the question!
var result = CompareDates(compareList, 3);
foreach (var intersectDate in result)
{
Console.WriteLine(intersectDate);
}
}
Hope this helps, I certainly had fun with it!
P.S. I forked your fiddle: https://dotnetfiddle.net/GUzhjh. This contains the modified version of your original program, so you should be able to play around with it a bit.
Upvotes: 1
Reputation: 11883
Here is a start at an algorithm:
Upvotes: 0