2010-07-02 3 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

+0

@ Начальная и конечная дата @skyfoot - это начало и конец любого диапазона дат, найденного в исходных данных. – RYFN

+0

Но тогда не должно быть «01/02/2010 по 03/02/2010» и «03/02/2010 по 06/02/2010» – skyfoot

ответ

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

2

ПРИМЕЧАНИЕ Предыдущий ответ удален.

EDIT Попробуйте этот пересмотренный один:

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

+0

Там достаточно кода, чтобы заставить голову вращаться! Похоже, что процедурный подход может быть проще поддерживать и понимать. –

+0

Я согласен, чтобы делать все, что ему нужно, я не думаю, что Linq действительно подходит. –

Смежные вопросы