I'm facing an interesting problem:
Is it possible to "des-overlap" theses ranges? That is, to generate:
Maybe I can make this a bit more graphical. This is what I have first:
a |------------------------------|
b |-------------------|
c |-----------------|
This is what I would like to obtain:
|------|---------|-------|-----|-----|
a a,c a,b,c a,b b
I found a solution that kind of works, but which is not elegant:
Can you think of a better solution? I'm working with C# but any language independent idea would be much appreciated. Thanks!
I would
I definitely did a bad job of explaining it. I'll be writing some code for this soon. But until then, here's what would happen in your solution:
a |------------------------------|
b |-------------------|
c |-----------------|
1. Start at day 1, add A to open ranges list, which now contains [A] 2. Move to the start of C. First RESULT RANGE: A range from Day 1 to C's start, with a value A. (what is in your open ranges list) 3. Add C to the open ranges list, which now contains [A,C] 4. Move to the start of B. Second RESULT RANGE: A range from C's start to B's start, with a value A,C. (what is in your open ranges list) 5. Add B to the open ranges list, which now contains [A,C,B] 6. Move to the end of C. Third RESULT RANGE: A range from B's start to C's end, with a value of A,C,B. 7. Remove C from the open ranges list, which now contains [A,B] 8. Move to the end of A. Fourth RESULT RANGE: A range from C's end to A's end, with a value of A,B 9. Remove A from the open ranges list, which now contains [B] 10. Move to the end of A. Fourth RESULT RANGE: A range from A's end to B's end, with a value of B RESULT RANGES 1. from Day 1 to C's start: A 2. from C's start to B's start: A,C 3. from B's start to C's end: A,C,B 4. from C's end to A's end: A,B 5. from A's end to B's end: B
You could do this:
Here are the first few steps, using the sample data + another range D:
("processed" ranges indicated by **double stars**
)
a |------------------------------|
b |-------------------|
c |-----------------|
d |------------------------|
1. Process A Output ranges: |--------------A---------------| 2. Process B - Start of B is in **A**; split A in two: |-------A-------|------AB------| - End of B is after any output range range; creating new output range at end |-------A-------|------AB------|---B---| - Nothing was/is in between **A** and (nothing) 3. Process C - Start of C is in **A**; split A in two: |---A----|--AC--|------AB------|---B---| - End of C is in **AB**; split AB in two: |---A----|--AC--|--ABC--|--AB--|---B---| - There were/are no ranges between **A** and **AB** 4. Process D - Start of D is in **A**; split A in two: |-A-|-AD-|--AC--|--ABC--|--AB--|---B---| - End of D is in **AB**; split AB in two: |-A-|-AD-|--AC--|--ABC--|ABD|AB|---B---| - Ranges AC and ABC were/are in between **A** and **AB** |-A-|-AD-|--ACD-|-ABCD--|ABD|AB|---B---| Final output: |-A-|-AD-|--ACD-|-ABCD--|ABD|AB|---B---|
You may want to have look at Interval Trees.
I have code that does this. It relies on the input-set being ordered by from
and then to
(ie. if it was SQL, it would be ORDER BY from_value, to_value
), but after that it is quite optimal.
My implementation basically does what @Justin L.'s answer describes, so if you just want a textual description, look at his answer for the algorithm.
The code is here: LVK.DataStructures, and the files you want to look at are:
To use it:
List<Range<DateTime>> ranges = ...
var slices = ranges.Slice();
This will give you one new range for each slice, and for each such range you would have a .Data property containing references back to the contributing ranges.
ie. for your original example, you would get exactly what you want, individual ranges, with the contributing ranges a, b, c etc. in the .Data property.
The classes might use other portions of my class library, which is all there. If you decide to use it, just copy out the portions you want to use.
If you're only interested in the implementation, it can be found in the RangeExtensions.cs file, line 447 and onwards, InternalSlice method.
You could:
For naming the new ranges, it would make sense to have the list of original range names that the current new range contains, and each time you hit an end date, remove the old range name from the list, and each tiem you hit a start date, add it's name to the list.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With