2011-01-16 16 views
31

ho una collezione di oggetti che includono una variabile TimeSpan:Somma di TimeSpan in C#

MyObject 
{ 
    TimeSpan TheDuration { get; set; } 
} 

voglio usare LINQ per riassumere quei tempi. Ovviamente, (da r in MyCollection seleziona r.TheDuration) .Sum(); non funziona!

Sto pensando di cambiare il tipo di dati di TheDuration in un file int e quindi di sommarlo e convertirlo in un TimeSpan. Ciò sarà disordinato perché ogni TheDuration nella mia collezione viene utilizzato come punto di riferimento da qualche altra parte.

Qualche suggerimento su questa somma?

risposta

74

Sfortunatamente, non c'è un sovraccarico di Sum che accetta uno IEnumerable<TimeSpan>. Inoltre, non esiste un modo corrente per specificare i vincoli generici basati sull'operatore per i parametri di tipo, quindi, anche se TimeSpan è un sommario "nativo", questo fatto non può essere raccolto facilmente dal codice generico.

Una possibilità potrebbe essere quella, come dici tu, riassumere un integrale di tipo equivalente al periodo, invece, e poi girare che somma in un TimeSpan di nuovo. La proprietà ideale per questo è TimeSpan.Ticks, che round-viaggi in modo accurato. Ma non è necessario cambiare il tipo di proprietà sulla tua classe; si può solo progetto:

var totalSpan = new TimeSpan(myCollection.Sum(r => r.TheDuration.Ticks)); 

In alternativa, se si vuole aderire al gestore del TimeSpan + a fare la somma, è possibile utilizzare l'Aggregate dell'operatore:

var totalSpan = myCollection.Aggregate 
       (TimeSpan.Zero, 
       (sumSoFar, nextMyObject) => sumSoFar + nextMyObject.TheDuration); 
+1

Solo una piccola correzione. Dovrebbe essere 'var totalSpan = new TimeSpan (myCollection.Sum (r => r.Ticks)); nel caso in cui 'myCollection è' Elenco () '. –

0

È possibile utilizzare .Aggregate piuttosto di .Sum, e passargli una funzione di somma di tempo che scrivi, come questa:

TimeSpan AddTimeSpans(TimeSpan a, TimeSpan b) 
    { 
     return a + b; 
    } 
+0

@Ani: Sì, quindi ...? – CesarGon

0

Ecco quello che ho provato e ha funzionato:

System.Collections.Generic.List<MyObject> collection = new List<MyObject>(); 
MyObject mb = new MyObject(); 
mb.TheDuration = new TimeSpan(100000); 
collection.Add(mb); 
mb.TheDuration = new TimeSpan(100000); 
collection.Add(mb); 
mb.TheDuration = new TimeSpan(100000); 
collection.Add(mb); 
var sum = (from r in collection select r.TheDuration.Ticks).Sum(); 
Console.WriteLine(sum.ToString()); 
//here we have new timespan that is sum of all time spans 
TimeSpan sumedup = new TimeSpan(sum); 


public class MyObject 
{ 
    public TimeSpan TheDuration { get; set; } 
} 
+0

equivale alla prima soluzione di @Ani, solo con un'espressione di query invece di notazione a punti – BrokenGlass

+0

@Broken Glass un po '.. quando ho postato questa risposta c'erano già 2 risposte ..;) I ragazzi sono molto veloci qui –

1

ho messo questo in una classe per aggiungere un metodo di estensione ad una collezione di timespans:

public static class Extensions: 
{ 
    public static TimeSpan TotalTime(this IEnumerable<TimeSpan> TheCollection) 
    { 
     int i = 0; 
     int TotalSeconds = 0; 

     var ArrayDuration = TheCollection.ToArray(); 

     for (i = 0; i < ArrayDuration.Length; i++) 
     { 
      TotalSeconds = (int)(ArrayDuration[i].TotalSeconds) + TotalSeconds; 
     } 

     return TimeSpan.FromSeconds(TotalSeconds); 
    } 
} 

Così ora, posso scrivere TotalDuration = (la mia query LINQ che restituisce un insieme di tempi) .TotalTime();

Voilà!

+1

dovresti avere * chiesto * per un metodo di estensione quindi ;-) – BrokenGlass

+1

"Voglio usare linq per sommare quei tempi." - hai cambiato idea? –

+0

Sto ancora utilizzando linq: posso interrogare i miei dati e quindi aggiungere .TotalTime(); alla fine della query e funziona. Sto ancora imparando il framework come puoi vedere. – frenchie

28

Questo metodo funziona bene (codice basato sulla risposta di Ani)

public static class StatisticExtensions 
{  
    public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> selector) 
    { 
     return source.Select(selector).Aggregate(TimeSpan.Zero, (t1, t2) => t1 + t2); 
    } 
} 

Usage:

Se Periodi è un elenco di oggetti con una proprietà Durata

TimeSpan total = Periods.Sum(s => s.Duration) 
+1

+1 per la risposta più pulita. –

0

Credo che questo sia il estensione LINQ più pulita:

public static class LinqExtensions 
{ 
    public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> func) 
    { 
     return new TimeSpan(source.Sum(item => func(item).Ticks)); 
    } 
} 

L'uso è lo stesso:

TimeSpan total = Periods.Sum(s => s.Duration) 
0

Questo funziona sia per una collezione, e una proprietà all'interno di una collezione;

void Main() 
{ 
    var periods = new[] { 
     new TimeSpan(0, 10, 0), 
     new TimeSpan(0, 10, 0), 
     new TimeSpan(0, 10, 0), 
    }; 
    TimeSpan total = periods.Sum(); 
    TimeSpan total2 = periods.Sum(p => p); 

    Debug.WriteLine(total); 
    Debug.WriteLine(total2); 

    // output: 00:30:00 
    // output: 00:30:00 
} 


public static class LinqExtensions 
{ 
    public static TimeSpan Sum(this IEnumerable<TimeSpan> timeSpanCollection) 
    { 
     return timeSpanCollection.Sum(s => s); 
    } 

    public static TimeSpan Sum<TSource>(this IEnumerable<TSource> source, Func<TSource, TimeSpan> func) 
    { 
     return new TimeSpan(source.Sum(item => func(item).Ticks)); 
    } 
}