だから、我々はこの一般的なイテレータ機能から始めましょう。これは、2つの項目を受け入れてブール値を返すシーケンスと述語を取ります。それはソースから項目を読み込み、項目はその前の項目とともに述語に基づいて真を返し、次の項目は「次のグループ」になります。 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;
}
ここでまとめておきます。まず、私たちは日を注文します。 (もし彼らがいつも来てくれたら、あなたはその部分を取り除くことができます。)そして、私たちは各項目が前の仕事の次の日である間、連続する項目をグループ化します。
次に、NonWorkingDay
に連続する日付のIEnumerable<DateTime>
を設定するだけです。そのため、開始日は最初の日付であり、Days
はシーケンスのカウントです。通常First
とCount
の両方を使用すると、元のシーケンスが2回繰り返されるため、GroupWhile
によって返されるシーケンスは実際にはList
ですが、複数回反復することは問題ではなく、Count
は偶数です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(),
});
}
素敵なパズル!... – spender
グループの週にしてグループ内のアイテムを数えてください。週ごとのグループ分けはこちらhttp://stackoverflow.com/questions/8561782/how-to-group-dates-by-week – bUKaneer
いいえ、そこに月曜日金曜日の2.5週間を言うかもしれないので、 – jmasterx