2013-03-14 17 views
13

Ho un List<DateTime> dates;Raggruppamento Contiguo Date

ho una classe che ha:

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

Sto cercando di capire un modo pulito per raggrupparli.

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

} 

Nota: se c'è un NWD il Venerdì e la prossima è Lunedi essi devono essere raggruppati. I fine settimana non sono considerati.

Per esempio se ho

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

Il risultato sarebbe:

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

Esiste un modo per fare questo (senza avere un gruppo di variabili contatore) e l'utilizzo di .Select o. Dove o qualcosa.

Grazie

+4

Bel puzzle! ... – spender

+0

Puoi raggruppare per settimana e poi contare gli articoli nel gruppo? Raggruppamento per settimana può essere trovato qui http://stackoverflow.com/questions/8561782/how-to-group-dates-by-week – bUKaneer

+0

No, perché ci potrebbe essere dire 2,5 settimane di lunedì-venerdì in là – jmasterx

risposta

17

Quindi, inizieremo con questa funzione iteratore generico. Richiede una sequenza e un predicato che accetta due elementi e restituisce un valore booleano. Leggerà gli elementi dalla fonte e mentre un elemento, insieme al suo elemento precedente, restituisce true in base al predicato, l'elemento successivo sarà nel "gruppo successivo". Se restituisce false, il gruppo precedente è pieno e viene avviato il gruppo successivo.

Avremo anche bisogno di questo semplice metodo di supporto che ottiene il giorno lavorativo successivo in base a una data. Se vuoi incorporare le vacanze, passa da banale a piuttosto difficile, ma è lì che andrebbe la logica.

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; 
} 

Ora per mettere tutto insieme. Per prima cosa ordiniamo i giorni. (Se si assicura che vengano sempre ordinati, è possibile rimuovere quella parte). Quindi raggruppiamo gli articoli consecutivi mentre ogni articolo è il giorno lavorativo successivo del precedente.

Poi tutto quello che dobbiamo fare è girare un IEnumerable<DateTime> date consecutive in un NonWorkingDay. Per questo la data di inizio è la prima data e Days è il conteggio della sequenza. Mentre normalmente usando sia First sia Count, iterare due volte la sequenza sorgente, sappiamo che la sequenza restituita da GroupWhile è in realtà un List sotto il cofano, quindi iterarlo più volte non è un problema, e ottenere lo Count è pari a 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

Genius assoluto, ben fatto signore! ; o) – bUKaneer

+0

@plutonix alcuni potrebbero non essere immediatamente ovvi, ma in questo caso è praticamente solo copia/incolla ... – Servy