Вы можете написать метод расширения, придерживаясь стиля LINQ. JoinBy
действует как аналитическая функция lag . Требуется orderBy
и keySelector
для определения смежных значений для оценки. join
используется, чтобы решить, когда смежные значения могут быть объединены.
public static class LinqExtension
{
public static IEnumerable<IEnumerable<TSource>> JoinBy<TSource, TOrderKey, TKey>(
this IEnumerable<TSource> source,
Func<TSource, TOrderKey> orderBy,
Func<TSource, TKey> keySelector,
Func<TKey, TKey, bool> join)
{
var results = new List<List<TSource>>();
var orderedSource = new List<TSource>(source).OrderBy(orderBy).ToArray();
if (orderedSource.Length > 0)
{
var group = new List<TSource> { orderedSource[0] };
results.Add(group);
if (orderedSource.Length > 1)
{
for (int i = 1; i < orderedSource.Length; i++)
{
var lag = orderedSource[i - 1];
var current = orderedSource[i];
if (join(keySelector(lag), keySelector(current)))
{
group.Add(current);
}
else
{
group = new List<TSource> { current };
results.Add(group);
}
}
}
}
return results;
}
}
.. и использовать его для группировки смежных проектов ..
public class Project
{
public string Name { get; set; }
public DateTime Begin { get; set; }
public DateTime End { get; set; }
}
[TestMethod]
public void TestCase1()
{
var projects = new List<Project>() {
new Project { Name = "A", Begin = new DateTime(2000, 1, 1), End = new DateTime(2000, 12, 31) }
, new Project { Name = "B", Begin = new DateTime(2001, 1, 1), End = new DateTime(2001, 12, 31) }
, new Project { Name = "C", Begin = new DateTime(2010, 1, 1), End = new DateTime(2010, 12, 31) }
, new Project { Name = "D", Begin = new DateTime(2010, 6, 1), End = new DateTime(2010, 7, 1) }
};
var grouped = projects.JoinBy(
x => x.Begin,
x => (begin: x.Begin, end: x.End),
(x1, x2) => x2.begin <= x1.end.AddDays(1) && x1.begin <= x2.end.AddDays(1));
var builder = new StringBuilder();
foreach (var grp in grouped)
{
builder.AppendLine(string.Join(", ", grp.Select(x => x.Name)));
}
var rendered = builder.ToString();
// rendered =>
// A, B
// C, D
}
РЕДАКТИРОВАТЬ: еще один тестовый пример из комментариев
[TestMethod]
public void TestCase2_FromComments()
{
var projects = new List<Project>() {
new Project { Name = "A", Begin = new DateTime(2019, 4, 2), End = new DateTime(2019, 8, 17) }
, new Project { Name = "B", Begin = new DateTime(2019, 6, 1), End = new DateTime(2019, 7, 1) }
};
var grouped = projects.JoinBy(
x => x.Begin,
x => (begin: x.Begin, end: x.End),
(x1, x2) => x2.begin <= x1.end.AddDays(1) && x1.begin <= x2.end.AddDays(1));
var builder = new StringBuilder();
foreach (var grp in grouped)
{
builder.AppendLine(string.Join(", ", grp.Select(x => x.Name)));
}
var rendered = builder.ToString();
// rendered =>
// A, B
}