13

私はList<DateTime> dates;

私は持っているクラスを持っています:

class NonWorkingDay
{
   public DateTime Start;
   public int Days;
}

それらをグループ化するきれいな方法を見つけようとしています。

public List<NonWorkingDay> GetContiguousDates(List<DateTime> dates)
{

}

注: 金曜日に NWD があり、次が月曜日である場合は、グループ化する必要があります。週末は考慮されません。

たとえば、私が持っている場合

September 3 2013
September 20 2013
September 23 2013
September 24 2013
September 30 2013
October 1  2013

出力は次のようになります。

Start = September 3 2013, Days = 1
Start = September 20 2013, Days = 3 //weekend got skipped
Start = September 30 2013, Days = 2

これを行う方法はありますか(カウンター変数の束を持たずに).Selectまたは.Whereなどを使用します。

ありがとう

4

1 に答える 1

20

したがって、この一般的なイテレータ関数から始めます。シーケンスと、2 つの項目を受け入れてブール値を返す述語を使用します。ソースからアイテムを読み取り、アイテムが前のアイテムとともに述語に基づいて 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を使用してソース シーケンスを 2 回反復しますが、 によって返されるシーケンスが実際には内部Countであることがわかっているため、複数回反復しても問題はなく、 を取得しても O(1) です。GroupWhileListCount

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(),
                });
}
于 2013-03-14T16:16:28.883 に答える