tags:

views:

112

answers:

1

I need to calculate the location of intersections between multiple date ranges, and the number of overlapping intersections. Then I need to show which date/time ranges overlap each of those intersecting sections. It is slightly more complicated than that so I'll do my best to explain by providing an example. I am working in VB.Net, but C# examples are acceptable as well as I work in both.

We have several high risk tasks that involve the same system. Below I have three example jobs named HR1/2/3/4 with start and end date/times.

  • HR1 {1/6/2010 10:00 - 1/6/2010 15:00}
  • HR2 {1/6/2010 11:00 - 1/6/2010 18:00}
  • HR3 {1/6/2010 12:00 - 1/6/2010 14:00}
  • HR4 {1/6/2010 18:00 - 1/6/2010 20:00}

What I want the end result to be is shown below. I am having trouble describing it any way but by example.

  • HRE1 {1/6/2010 10:00 - 1/6/2010 11:00} - Intersects 1
  • {End Time Split 1, for readability only, not needed in solution}
  • HRE1 {1/6/2010 11:00 - 1/6/2010 12:00} - Intersects 2
  • HRE2 {1/6/2010 11:00 - 1/6/2010 12:00} - Intersects 2
  • {End Time Split 2, for readability only, not needed in solution}
  • HRE1 {1/6/2010 12:00 - 1/6/2010 14:00} - Intersects 3
  • HRE2 {1/6/2010 12:00 - 1/6/2010 14:00} - Intersects 3
  • HRE3 {1/6/2010 12:00 - 1/6/2010 14:00} - Intersects 3
  • {End Time Split 3, for readability only, not needed in solution}
  • HRE1 {1/6/2010 14:00 - 1/6/2010 15:00} - Intersects 2
  • HRE2 {1/6/2010 14:00 - 1/6/2010 15:00} - Intersects 2
  • {End Time Split 4, for readability only, not needed in solution}
  • HRE2 {1/6/2010 15:00 - 1/6/2010 18:00} - Intersects 1
  • {End Time Split 5, for readability only, not needed in solution}
  • HR4 {1/6/2010 18:00 - 1/6/2010 20:00} - Intersects 1

Any help would be greatly appreciated.

+5  A: 
var timePoints = (from r in ranges select r.Start)
    .Concat(from r in ranges select r.End)
    .Distinct().OrderBy(dt => dt).ToArray();

var intersections = from i in Enumerable.Range(0, timePoints.Length - 1)
                    let start = timePoints[i]
                    let end = timePoints[i + 1]
                    from range in ranges
                    where range.Start <= start && range.End >= end
                    select new { Range = range, Start = start, End = end };

EDIT: Modified code that counts intersections:

var timePoints = (from r in ranges select r.Start)
    .Concat(from r in ranges select r.End)
    .Distinct().OrderBy(dt => dt).ToArray();

var intersectionGroups = from i in Enumerable.Range(0, timePoints.Length - 1)
                         let start = timePoints[i]
                         let end = timePoints[i + 1]
                         select new
                         {
                             Start = start,
                             End = end,
                             Ranges =
                                 from range in ranges
                                 where range.Start <= start && range.End >= end
                                 select range
                         };

var intersections = from intGroup in intersectionGroups
                    let count = intGroup.Ranges.Count()
                    from range in intGroup.Ranges
                    select new
                    {
                        Range = range,
                        Start = intGroup.Start,
                        End = intGroup.End,
                        Count = count
                    };

I don't know what do you want to do with the result, but it may be better to use intersectionGroups rather than intersections.

svick
WOW! This is very close to what I am looking for. At first I didn't get it but after running through it in VS it made sense after a minute. Two small things come to mind, and one is my fault since I didn't put it in the original post.First, it doesn't tell me how many other ranges overlap it. So for the 11:00 to 12:00 time range the value would be 2. Maybe an additional property called OverlapCount or something.Second: This is the case I didn't mention. Items that are in the array that don't overlap anything are dropped. Is there a way we can keep them? I added this to the post.
Patricker
My code doesn't drop ranges that don't overlap with anything (like HR4 in your example). I'll have a look at the counts.
svick
svick, that my friend is a thing of beauty. I actually had developed a solution to the problem before posting but it was so horid I knew there had to be a simple solution. Once again linq saves the day. Thanks again for the assistance svick.
Patricker