Ответ 1
Итак, мы начнем с этой общей функции итератора. Он принимает последовательность и предикат, который принимает два элемента и возвращает логическое значение. Он будет считываться в элементах из источника, и в то время как элемент вместе с предыдущим элементом возвращает true на основе предиката, следующий элемент будет в "следующей группе". Если он возвращает false, предыдущая группа заполняется и начинается следующая группа.
public static IEnumerable<IEnumerable<T>> GroupWhile<T>(this IEnumerable<T> source
, Func<T, T, bool> predicate)
{
using (var iterator = source.GetEnumerator())
{
if (!iterator.MoveNext())
yield break;
List<T> currentGroup = new List<T>() { iterator.Current };
while (iterator.MoveNext())
{
if (predicate(currentGroup.Last(), iterator.Current))
currentGroup.Add(iterator.Current);
else
{
yield return currentGroup;
currentGroup = new List<T>() { iterator.Current };
}
}
yield return currentGroup;
}
}
Нам также понадобится этот простой вспомогательный метод, который будет получать следующий рабочий день на основе даты. Если вы хотите включить праздники, то это от тривиального до довольно сложного, но в том, что логика будет идти.
public static DateTime GetNextWorkDay(DateTime date)
{
DateTime next = date.AddDays(1);
if (next.DayOfWeek == DayOfWeek.Saturday)
return next.AddDays(2);
else if (next.DayOfWeek == DayOfWeek.Sunday)
return next.AddDays(1);
else
return next;
}
Теперь все вместе. Сначала мы заказываем дни. (Если вы гарантируете, что они всегда приходят в заказе, вы можете удалить эту часть.) Затем мы группируем последовательные элементы, а каждый элемент - следующий рабочий день предыдущего.
Тогда нам нужно только сделать IEnumerable<DateTime>
последовательных дат в NonWorkingDay
. Для этого дата начала - это первая дата, а Days
- это счетчик последовательности. Обычно, используя как First
, так и Count
, будет дважды повторяться последовательность источников, мы знаем, что последовательность, возвращаемая GroupWhile
, на самом деле является List
под капотом, поэтому повторение ее несколько раз не является проблемой, и получение Count
равно O (1).
public IEnumerable<NonWorkingDay> GetContiguousDates(IEnumerable<DateTime> dates)
{
return dates.OrderBy(d => d)
.GroupWhile((previous, next) => GetNextWorkDay(previous).Date == next.Date)
.Select(group => new NonWorkingDay
{
Start = group.First(),
Days = group.Count(),
});
}