2015-05-14 117 views
2

我有個約會週期名單和那些期限不重疊結合連續日期期間

|StartDate| EndDate| 
| null | 1/12 | 
| 2/12 | null | 
| null | 4/12 | 
| 6/12 | 8/12 | 
| 9/12 | null | 
| null | 10/12 | 
| 11/12 | null | 

我有那些時間段相結合,顯示如下列表:

|StartDate| EndDate| 
| null | 1/12 | 
| 2/12 | 4/12 | 
| 6/12 | 8/12 | 
| 9/12 | 10/12 | 
| 11/12 | null | 

這裏是我的解決方案,但我認爲這不是一個聰明的辦法

 var dateList = periodList.SelectMany(x => new[] { 
      new {Date = x.Item1, type = "S"}, 
      new {Date = x.Item2, type = "E"} 
     }).Where(x => x.Date != null).OrderBy(x => x.Date).ToArray(); 

     var result = new List<Tuple<DateTime?, DateTime?>>(); 
     int i = 0; 
     do 
     { 
      if (i == 0 && dateList[i].type == "E") 
      { 
       result.Add(new Tuple<DateTime?, DateTime?>(null, dateList[i].Date)); 
      } 
      else if (i + 1 == dateList.Count() && dateList[i].type == "S") 
      { 
       result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, null)); 
      } 
      else 
      { 
       if (dateList[i].type == "S" && dateList[i+1].type == "E") 
       { 
        result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, dateList[i + 1].Date)); 
        i++; 
       }      
      } 
      i++; 
     } while (i < dateList.Count()); 
+1

我建議你創造一些與'性質DateRange'類'StartDate'和'EndDate'而不是使用元組無意義'Item1'和'Item2'性能。您還可以創建諸如「Intersects」之類的方法來檢查時間範圍是否相交,並使用「Intersetion」方法來獲取兩個範圍內的新時間範圍。 –

+0

是您的輸入列表中的日期順序(即較大的EndDate從未出現在較小的日期之前)? – publicgk

回答

0

我的解決辦法似乎LON ger,但它是清潔劑在我看來。我認爲你有類(的Tuple<DateTime?, DateTime?>代替)如下:

public class Period 
{ 
    public DateTime? StartDate { get; set; } 
    public DateTime? EndDate { get; set; } 
} 

日期添加到時期列表:

// Make list of periods ready 
List<Period> periodList = //Prepare periods; 

獲取開始日期和結束分別日期通過消除null ones:

// Get start dates 
List<DateTime> startDates = periodList 
    .Where(p => p.StartDate.HasValue) 
    .Select(p => p.StartDate.Value) 
    .ToList(); 

// Get end dates 
List<DateTime> endDates = periodList 
    .Where(p => p.EndDate.HasValue) 
    .Select(p => p.EndDate.Value) 
    .ToList(); 

然後做其他操作:

// Clear list of periods 
periodList.Clear(); 

// Add start dates which are bigger than LAST end date with NULL end date period 
startDates.Where(s => s > endDates.Max()) 
    .ToList() 
    .ForEach(s => periodList.Add(new Period() { StartDate = s, EndDate = null })); 

// Add end dates which are smaller than FIRST start date with NULL start date period 
endDates.Where(e => e < startDates.Min()) 
    .ToList() 
    .ForEach(e => periodList.Add(new Period() {StartDate = null, EndDate = e})); 

// Match other dates and add them to list 
startDates.Where(s => s < endDates.Max()) 
    .ToList() 
    .ForEach(s => periodList.Add(new Period() 
           { 
            StartDate = s, 
            EndDate = endDates.Where(e => e > s).Min() 
           })); 

// Oder period list 
periodList = periodList.OrderBy(p => p.StartDate).ToList(); 

您可以測試.NET小提琴演示here

我希望這會有所幫助。

0

以下代碼是使用

  1. 一個DateRange類型和
  2. 要麼DateTime.MinValueDateTime.MaxValue的定點值,以「保護」任何null第一開始日期或任何null最後結束日期的實現。

下面是一個創建DateRange值從可爲空DateTime值的兩個平行的列表列表中的方法:

public static List<DateRange> MakeRanges(List<DateTime?> list1, List<DateTime?> list2) 
{ 
    //Validate arguments to the function 
    if (list1 == null || list2 == null || list1.Count == 0 || 
     list2.Count == 0 || list1.Count != list2.Count) 
    { 
     throw new ArgumentException("Bad arguments passed to MakeRanges()."); 
    } 
    //If present, replace null start value of list1 with a sentinel 
    list1[0] = list1[0] ?? DateTime.MinValue; 
    //If present, replace null end value of list2 with a sentinel 
    list2[list2.Count - 1] = list2[list2.Count - 1] ?? DateTime.MaxValue; 

    //this expression does the heavy lifting. Match a start date with the closest non-null end-date 
    return list1.Where(s => s.HasValue).Select(startItem => new DateRange{ 
      Start = startItem, 
      End = list2.Find(e => (e.HasValue && (e > startItem.Value))).Value 
     }).ToList(); 
} 

下面是清除任何哨兵一DATERANGE類:

public class DateRange 
{ 
    private DateTime? _start; 
    private DateTime? _end; 

    public DateTime? Start 
    { 
     get { return _start; } 
     //get rid of any sentinel value 
     set { _start = value == DateTime.MinValue ? null : value; } 
    } 

    public DateTime? End 
    { 
     get { return _end; } 
     // get rid of any sentinel value 
     set { _end = value == DateTime.MaxValue ? null : value; } 
    } 
    //For checking results 
    public override string ToString() 
    { 
     return String.Format("{0}-{1}", Start.HasValue ? Start.Value.ToShortDateString() : "null", End.HasValue ? End.Value.ToShortDateString() : "null"); 
    } 
} 

而且這裏是一個控制檯應用程序主要作爲驅動程序:

public static void Main(string[] args) 
{ 
    List<DateTime?> list1 = new List<DateTime?>() { null, DateTime.Parse("2014-02-12"), null, DateTime.Parse("2014-06-12"), DateTime.Parse("2014-09-12"), null, DateTime.Parse("2014-11-12")}; 
    List<DateTime?> list2 = new List<DateTime?>() { DateTime.Parse("2014-01-12"), null, DateTime.Parse("2014-04-12"), DateTime.Parse("2014-08-12"), null, DateTime.Parse("2014-10-12"), null }; 

    List<DateRange> ranges = MakeRanges(list1, list2); 

    //Print out results 
    foreach (var range in ranges) 
    { 
     Console.WriteLine(range); 
    } 
    var pressAKeyToExit = Console.ReadKey(); 
} 

輸出是:

null-1/12/2014 
2/12/2014-4/12/2014 
6/12/2014-8/12/2014 
9/12/2014-10/12/2014 
11/12/2014-null 
0

假設約會總是爲了輸入列表中,以下可以是一個快速解決方案(不要」知道,如果它的速度更快):

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 

namespace SO30229368 
{ 
    class Program 
    { 
     private const string NullItem = "null"; 

     static void Main(string[] args) 
     { 
      var periodList = new List<Tuple<string, string>> 
      { 
       new Tuple<string, string>(NullItem, "1/12"), 
       new Tuple<string, string>("2/12", NullItem), 
       new Tuple<string, string>(NullItem, "4/12"), 
       new Tuple<string, string>("6/12", "8/12"), 
       new Tuple<string, string>("9/12", NullItem), 
       new Tuple<string, string>(NullItem, "10/12"), 
       new Tuple<string, string>("11/12", NullItem) 
      }; 

      var consecutiveList = GetConsecutive(periodList); 
      foreach (var tupleItem in consecutiveList) 
       Console.WriteLine("{0} - {1}", tupleItem.Item1, tupleItem.Item2); 

      Console.ReadLine(); 
     } 

     private static IEnumerable<Tuple<string, string>> GetConsecutive(List<Tuple<string, string>> periodList) 
     { 
      if (periodList == null) 
       throw new ArgumentNullException("periodList"); 
      if (periodList.Count == 0) 
       return new List<Tuple<string, string>>(); 

      var startList = periodList.Select(x => x.Item1).Where(y => y != NullItem).ToList(); 
      if (periodList.First().Item1 == NullItem) 
       startList.Insert(0, NullItem); 
      var endList = periodList.Select(x => x.Item2).Where(y => y != NullItem).ToList(); 
      if (periodList.Last().Item2 == NullItem) 
       endList.Add(NullItem); 

      Debug.Assert(startList.Count == endList.Count); 
      return Enumerable.Zip(startList, endList, (start, end) => new Tuple<string, string>(start, end)).ToList(); 
     } 
    } 
} 

雖然作爲由他人推薦,最好將對象建模爲自定義對象(例如, DateRange)而不是使用元組。

0

如果您確信

1)日期間隔是爲了

2)後,每個null結束日期旁邊的startDate將null

那麼一個簡單的方法可能應刪除所有中間值null

List<DateTime?> startDates = allStartDates(); // excluding null values, but including the first one. 
List<DateTime?> endDates = allEndDates(); // excluding null values, but including the last one. 

然後,您可以進行一對一匹配以獲取所有時間間隔。例如;

DateTime? startDate3 = startDates[2]; 
DateTime? endDate3 = endDates[2]; 
+0

這就是我建議(與代碼:) :) – publicgk

+0

@publicgk是似乎相同。我只是想給算法,而不是代碼。事實上,我沒有在答案中檢查所有的代碼。 – serdar

+1

很好。現在OP可以同時獲得算法和代碼。 – publicgk