I have these objects with a range and list of properties. My goal is to take a list of these objects and create a new list where no ranges overlap. When two ranges are overlapping the overlapping region can be replaced with an object with the union of the properties. I'm hoping a picture can explain this a little better than words can.
I was able to achieve this with the following code however it is rather clunky and uses a goto which I try to avoid. The methodology behind this code is to compare every pair of ranges and if there is an intersection, replace both RangeObjects with up to three RangeObjects:
- One for the part of rangeObject1 that doesn't intersect rangeObject2
- One for the part of rangeObject2 that doesn't intersect rangeObject1
- One for the intersection of rangeObject1 and rangeObject2 with unioned properties.
class RangeObject
{
RangeObject(int start, int end, List<object> properties)
{
Start = start;
End = end;
Properties = properties;
}
public int Start { get; set; }
public int End { get; set; }
public int Length { get => End - Start; }
public List<object> Properties { get; }
public bool IsInRange(int value)
{
return value > Start && value < End;
}
private List<RangeObject> UntangleRangeList(List<RangeObject> list)
{
List<RangeObject> list1 = new List<RangeObject>(list);
list1.Sort();
int start = 0;
startover:
for (int i = start; i < list1.Count; i++)
{
for (int j = i + 1; j < list1.Count; j++)
{
if (list1[i].Start < list1[j].End && list1[j].Start < list1[i].End)
{
list1.AddRange(UntangleRanges(list1[i], list1[j]));
list1.Remove(list1[j]);
list1.Remove(list1[i]);
goto startover;
}
}
start++;
}
list1.Sort();
return list1;
}
public List<RangeObject> UntangleRanges(RangeObject rangeObject1, RangeObject rangeObject2)
{
List<RangeObject> result = new List<RangeObject>();
//This is to reduce the number of if cases by allowing the asumption that rangeObject1 is first
if (rangeObject1.Start > rangeObject2.Start || ((rangeObject1.Start == rangeObject2.Start) && (rangeObject1.Length > rangeObject2.Length)))
{
RangeObject temp = rangeObject1;
rangeObject1 = rangeObject2;
rangeObject2 = temp;
}
if (rangeObject1.Start == rangeObject2.Start && rangeObject1.End == rangeObject2.End) //Ranges Equal
{
result.Add(new RangeObject(rangeObject1.Start, rangeObject1.End, rangeObject1.Properties.Union(rangeObject2.Properties).ToList()));//Return a RangeObject of Unioned Properties
}
else if(rangeObject1.IsInRange(rangeObject2.Start) && rangeObject1.IsInRange(rangeObject2.End))//Range2 contained in Range1
{
if (rangeObject1.Start != rangeObject2.Start)
{
result.Add(new RangeObject(rangeObject1.Start, rangeObject2.Start - rangeObject1.Start, rangeObject1.Properties));
}
result.Add(new RangeObject(rangeObject2.Start, rangeObject2.Length, rangeObject1.Properties.Union(rangeObject2.Properties).ToList()));
if (rangeObject1.End != rangeObject2.End)
{
result.Add(new RangeObject(rangeObject2.End, rangeObject1.End - rangeObject2.End, rangeObject1.Properties));
}
}
else if (rangeObject1.IsInRange(rangeObject2.Start))//Range2.Start contained in Range1
{
result.Add(new RangeObject(rangeObject1.Start, rangeObject2.Start - rangeObject1.Start, rangeObject1.Properties));
result.Add(new RangeObject(rangeObject2.Start, rangeObject1.End - rangeObject2.Start, rangeObject1.Properties.Union(rangeObject2.Properties).ToList()));
result.Add(new RangeObject(rangeObject1.End, rangeObject2.End - rangeObject1.End, rangeObject2.Properties));
}
else //No intersection, shouldn't happen
{
result.Add(rangeObject1);
result.Add(rangeObject2);
}
return result;
}
}
Any ideas on how to achieve this a little more elegantly? Or at least avoid the startover because when I cut up the ranges I end up relocating them to the end and changing the length/order of the list so it's hard to tell what ranges I've compared.
