, werden wir mit dieser generischen Iterator Funktion beginnen. Es benötigt eine Sequenz und ein Prädikat, das zwei Elemente akzeptiert und einen booleschen Wert zurückgibt. Es liest Elemente aus der Quelle ein und während ein Element zusammen mit seinem vorherigen Element basierend auf dem Prädikat "true" zurückgibt, wird das nächste Element in der "nächsten Gruppe" sein. Wenn sie false zurückgibt, ist die vorherige Gruppe voll und die nächste Gruppe wird gestartet.
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;
}
}
Wir brauchen auch diese einfache Hilfsmethode, die den nächsten Arbeitstag basierend auf einem Datum erhält. Wenn Sie auch Ferien integrieren wollen, geht es von trivial zu ziemlich hart, aber das ist, wo die Logik gehen würde.
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;
}
Nun, um alles zusammen zu setzen. Zuerst bestellen wir die Tage. (Wenn Sie sicherstellen, dass sie immer bestellt werden, können Sie diesen Teil entfernen.) Dann gruppieren wir die aufeinanderfolgenden Artikel, während jeder Artikel der nächste Arbeitstag des vorherigen ist.
Dann müssen wir nur einen IEnumerable<DateTime>
von aufeinanderfolgenden Daten in eine NonWorkingDay
drehen. Das Startdatum ist das erste Datum und Days
ist die Anzahl der Sequenz. Während normalerweise beide First
und Count
iterieren die Quellsequenz zweimal, wir wissen, dass die von GroupWhile
zurückgegebene Sequenz ist eigentlich eine List
unter der Haube, so iterieren es mehrmals ist kein Problem, und die Count
ist sogar 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(),
});
}
Schönes Puzzle! ... – spender
Könnten Sie GroupBy Woche und dann die Elemente in der Gruppe zählen? Gruppierung nach Wochen finden Sie hier http://stackoverflow.com/questions/8561782/how-to-group-dates-by-week – bUKaneer
Nein, denn es könnte sagen, 2,5 Wochen Montag-Freitag drin – jmasterx