J'ai une collection d'objets qui incluent une variable TimeSpan:
MyObject
{
TimeSpan TheDuration { get; set; }
}
Je souhaite utiliser LINQ pour faire la somme de ces moments . Bien sûr, (dans r dans MyCollection, sélectionnez r.TheDuration) .Sum (); ne fonctionne pas!
Je pense changer le type de données de TheDuration en int, puis le sommer et convertir la somme en TimeSpan. Ce sera désordonné, car chaque TheDuration de ma collection est utilisé ailleurs.
Une suggestion sur cette sommation?
Malheureusement, il n'y a pas de surcharge Sum
qui accepte un IEnumerable<TimeSpan>
. De plus, il n'existe actuellement aucun moyen de spécifier des contraintes génériques basées sur l'opérateur pour les paramètres de type. Ainsi, même si TimeSpan
est sommable "de manière native", ce fait ne peut pas être facilement pris en compte par le code générique.
Une option serait, comme vous le dites, de résumer un équivalent de type entier à la durée, puis de transformer à nouveau that sum en une TimeSpan
. La propriété idéale pour cela est TimeSpan.Ticks
, qui effectue un aller-retour avec précision. Mais il n'est pas du tout nécessaire de changer le type de propriété de votre classe. vous pouvez simplement projet:
var totalSpan = new TimeSpan(myCollection.Sum(r => r.TheDuration.Ticks));
Si vous souhaitez vous en tenir à l'opérateur +
de TimeSpan pour effectuer la somme, vous pouvez utiliser l'opérateur Aggregate
:
var totalSpan = myCollection.Aggregate
(TimeSpan.Zero,
(sumSoFar, nextMyObject) => sumSoFar + nextMyObject.TheDuration);
Cela fonctionne bien (code basé sur la réponse d'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);
}
}
Utilisation:
Si Periods est une liste d'objets avec une propriété Duration
TimeSpan total = Periods.Sum(s => s.Duration)
Je mets cela dans une classe pour ajouter une méthode d'extension à une collection de plages horaires:
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);
}
}
Alors maintenant, je peux écrire TotalDuration = (ma requête LINQ qui retourne une collection de timespan) .TotalTime ();
Voila!
Je crois que c'est l'extension LINQ la plus propre:
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'utilisation est la même:
TimeSpan total = Periods.Sum(s => s.Duration)
Vous pouvez utiliser .Aggregate
plutôt que .Sum
et lui transmettre une fonction de somme d'heures que vous écrivez, comme ceci:
TimeSpan AddTimeSpans(TimeSpan a, TimeSpan b)
{
return a + b;
}
Voici ce que j'ai essayé et cela a fonctionné:
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; }
}
Cela fonctionne à la fois pour une collection et pour une propriété dans une collection.
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));
}
}