2013-03-14 25 views
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 

有沒有辦法做到這一點(不有一大堆的計數器變量)和使用。選擇或。在哪裏或什麼。

謝謝

+4

不錯的益智!... – spender 2013-03-14 15:52:47

+0

莫非你的GroupBy周再算上組中的項目?分組按周可以在這裏找到http://stackoverflow.com/questions/8561782/how-to-group-dates-by-week – bUKaneer 2013-03-14 15:58:07

+0

沒有,因爲有可能是說2.5周的週一至週五在那裏 – jmasterx 2013-03-14 15:59:18

回答

17

所以,我們將從這個泛型迭代函數開始。它需要一個序列和一個謂詞來接受兩個項目並返回一個布爾值。它將從源文件中讀取項目,並且一個項目及其之前的項目根據謂詞返回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是序列的計數。雖然通常同時使用FirstCount會迭代源序列兩次,但我們碰巧知道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(), 
       }); 
} 
+5

絕對的天才,做得好先生! ; o) – bUKaneer 2013-03-14 16:28:21

+0

@plutonix有些可能不會立即明顯,但在這種情況下,它實際上只是複製/粘貼... – Servy 2016-05-24 19:34:15