这里有一个“双字典”实现,可以在不先排序范围的情况下进行合并。假设没有重叠和重复属性。重复属性将导致抛出异常。
public static IEnumerable<TSource> Consolidate<TSource, TProperty>(
this IEnumerable<TSource> source,
Func<TSource, TProperty> property1Selector,
Func<TSource, TProperty> property2Selector,
Func<TSource, TSource, TSource> combine)
{
var dict1 = source.ToDictionary(property1Selector);
var dict2 = source.ToDictionary(property2Selector);
if (dict1.Keys.Count == 0) yield break;
var first = dict2.Values.First();
var last = first;
var current = first;
while (true)
{
dict1.Remove(property1Selector(first));
dict2.Remove(property2Selector(first));
if (dict2.TryGetValue(property1Selector(first), out current))
{
first = current;
}
else
{
while (true)
{
if (dict1.TryGetValue(property2Selector(last), out current))
{
last = current;
dict1.Remove(property1Selector(last));
dict2.Remove(property2Selector(last));
}
else
{
yield return combine(first, last);
break;
}
}
if (dict1.Keys.Count == 0) break;
first = dict1.Values.First();
last = first;
}
}
}
使用示例:
var source = new List<(DateTime From, DateTime To)>()
{
(new DateTime(2019, 1, 10), new DateTime(2019, 1, 12)),
(new DateTime(2019, 3, 10), new DateTime(2019, 3, 14)),
(new DateTime(2019, 1, 12), new DateTime(2019, 1, 13)),
(new DateTime(2019, 3, 5), new DateTime(2019, 3, 10)),
};
var consolidated = source
.Consolidate(r => r.From, r => r.To, (r1, r2) => (r1.From, r2.To))
.OrderBy(r => r.From)
.ToList();
foreach (var range in consolidated)
{
Console.WriteLine($"{range.From:yyyy-MM-dd} => {range.To:yyyy-MM-dd}");
}
输出:
2019年1月10日 => 2019年1月13日
2019年3月5日 => 2019年3月14日