How it works
Interval merging combines ranges that overlap under a chosen endpoint policy. Sorting by start makes the next possible overlap easy to inspect: only the most recent merged interval can connect to the next input interval. The sample treats intervals as closed and merges ranges that touch at an endpoint.
The ranges one through three and two through six become one through six. Eight through ten and ten through twelve become eight through twelve because ten belongs to both under the stated policy. The output therefore depends on a deliberate interpretation of endpoints, not merely on a convenient comparison operator.
When merging, the ending boundary must be the maximum of both ends. Replacing it blindly with the next interval's end would shrink a containing range, such as one through ten followed by two through three. The sorted fold should preserve the union of all processed interval contents.
The implementation rejects a reversed interval rather than silently swapping its endpoints. Another application could choose normalization, but that would be a different contract. Time ranges, inclusive integer ranges, and half-open text spans can require different overlap and adjacency rules, so avoid reusing a helper without checking its units and boundaries.
Practice with a containing interval, equal intervals, disjoint ranges, and a single-point interval. Then change the policy so merely touching ranges remain separate and identify the exact condition that must change. Predict both outputs before running the two variants.
Finally, verify that merged ranges are ordered and no longer overlap according to the chosen policy. Compare the covered integer points for a small bounded example before and after merging. Sorting dominates the usual running cost, while the fold itself is linear; the correctness argument comes from preserved coverage and the monotonic order of interval starts.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using System.Runtime.CompilerServices;
var intervals = new[] { (Start: 8, End: 10), (Start: 1, End: 3), (Start: 2, End: 6), (Start: 10, End: 12) };
var merged = new List<(int Start, int End)>();
foreach (var interval in intervals.OrderBy(item => item.Start).ThenBy(item => item.End)) {
if (interval.Start > interval.End) throw new ArgumentException("Reversed interval");
if (merged.Count == 0 || interval.Start > merged[^1].End) merged.Add(interval);
else merged[^1] = (merged[^1].Start, Math.Max(merged[^1].End, interval.End));
}
Console.WriteLine(string.Join(",", merged.Select(item => $"{item.Start}-{item.End}")));
Expected output
1-6,8-12
This contract treats touching endpoints as connected intervals.