2010-07-02 85 views
1

給定單日日期(dd-mm-yyyy)的數據表示(如下所示)。將單個日期的收集平鋪到日期範圍內

什麼是最優雅的方式將其轉換爲範圍集合,爲每種類型分組連續日期(右側顯示)?

我們可以假設初始數據按類型ID排序,然後是日期。

TypeID | Date   -> TypeID | Start  | End 
1  | 01/02/2010   1  | 01/02/2010 | 03/02/2010 
1  | 02/02/2010   2  | 03/02/2010 | 04/02/2010 
1  | 03/02/2010   2  | 06/02/2010 | 06/02/2010 
2  | 03/02/2010 
2  | 04/02/2010 
2  | 06/02/2010 

我不是特別在我的LINQ,但我想這可能是要走的路?

任何援助將不勝感激!

+0

你如何確定開始和結束日期是什麼? – skyfoot 2010-07-02 08:55:35

+0

@skyfoot開始和結束日期是在初始數據中找到的任何日期範圍的開始和結束。 – RYFN 2010-07-02 09:10:26

+0

但不應該是「01/02/2010至03/02/2010」和「03/02/2010至06/02/2010」 – skyfoot 2010-07-02 10:14:48

回答

2

確定有人會想出一個整潔的LINQ解決方案,但這裏是我的舊式解決方案。當然它有問題,不會與每個組合應對,拼湊起來,解決了這一問題,但以任何方式:-(

internal class SourceData 
{ 
    public int TypeId { get; set; } 
    public DateTime Date { get; set; } 
} 

internal class Result 
{ 
    public int TypeId { get; set; } 
    public DateTime StartDate { get; set; } 
    public DateTime EndDate { get; set; } 
} 

class Program 
{ 

    static void Main() 
    { 

     var a = new List<SourceData> { 
      new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 01)}, 
      new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 02)}, 
      new SourceData {TypeId = 1, Date = new DateTime(2010, 02, 03)}, 
      new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 03)}, 
      new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 04)}, 
      new SourceData {TypeId = 2, Date = new DateTime(2010, 02, 06)} 
     }; 

     var results = new List<Result>(); 
     int currentTypeId = 1; 
     var rangeEndDate = new DateTime(); 

     DateTime rangeStartDate = a[0].Date; 
     DateTime currentDate = a[0].Date; 

     for (int i = 1; i < a.Count() ; i++) 
     { 

      if (a[i].TypeId != currentTypeId) 
      { 
       results.Add(new Result() { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate }); 
       currentTypeId += 1;      
       rangeStartDate = a[i].Date; 
      } 

      TimeSpan tSpan = a[i].Date - currentDate; 
      int differenceInDays = tSpan.Days; 

      if(differenceInDays > 1) 
      { 
       results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = a[i-1].Date }); 
       rangeStartDate = a[i].Date; 
      } 

      rangeEndDate = a[i].Date; 
      currentDate = a[i].Date; 
     } 

     results.Add(new Result { TypeId = currentTypeId, StartDate = rangeStartDate, EndDate = rangeEndDate }); 

     Console.WriteLine("Output\n"); 
     foreach (var r in results) 
      Console.WriteLine(string.Format("{0} - {1} - {2}",r.TypeId,r.StartDate.ToShortDateString(),r.EndDate.ToShortDateString())); 

     Console.ReadLine(); 

    } 
} 

不優雅提供了以下的輸出: -

輸出

1 - 01/02/2010 - 03/02/2010

2 - 03/02/2010 - 04/02/2010

2 - 06/02/2010 - 06/02/2010

+0

這與我目前使用的方法幾乎相同,它可以完成這項工作,而且它的可讀性強,所以不要把自己放在心上:) – RYFN 2010-07-02 10:35:24

2

備註先前的答案已移除。

編輯試試這個修訂後的一個:

public static IEnumerable<TAnonymous> Flatten<T, TAnonymous>(
    this IEnumerable<T> enumerable, 
    Func<T, T, bool> criteria, 
    Func<T, T, TAnonymous> selector, 
    Func<TAnonymous, T, T, TAnonymous> modifier) 
{ 
    var list = new List<TAnonymous>(); 

    T last = default(T); 
    bool first = true; 
    bool created = false; 

    TAnonymous current = default(TAnonymous); 

    Action<T, T> action = (a, b) => 
          { 
           if (criteria(a, b)) { 
            if (created) { 
             current = modifier(current, a, b); 
            } else { 
             current = selector(a, b); 
             created = true; 
            } 
           } else { 
            if (created) { 
             list.Add(current); 
             current = default(TAnonymous); 
             created = false; 
            } else { 
             list.Add(selector(a, a)); 
            } 
           } 
          }; 

    foreach (T item in enumerable) { 
     if (first) { 
      first = false; 
      last = item; 
      continue; 
     } 

     action(last, item); 
     last = item; 
    } 

    action(last, last); 

    if (created) 
     list.Add(current); 

    return list; 
} 

古稱:

var filtered = list.Flatten(
    (r1, r2) => ((r2.Date - r1.Date).Days <= 1 && r1.TypeID == r2.TypeID), 
    (r1, r2) => new { r1.TypeID, Start = r1.Date, End = r2.Date }, 
    (anon, r1, r2) => new { anon.TypeID, anon.Start, End = r2.Date }); 

應該有希望的工作......我們在做什麼這一次,是操作分解成階段,首先匹配一個條件,然後創建一個新條目(選擇器)或更新以前創建的條目(修飾符)。

+0

感謝您抽出時間寫出來,這很有趣,尤其是考慮到我不太瞭解LINQ!另外,我認爲這是一個稍微複雜一些的問題,而不像你剛纔所說的那樣。相當有趣:) – RYFN 2010-07-02 10:28:44

+0

有足夠的代碼讓你的頭旋轉!看起來像程序性方法可能更容易維護和理解。 – 2010-07-02 10:58:37

+0

我同意,爲了做他所需要的,我認爲Linq並不適合。 – 2010-07-02 11:04:58