[英]Sum of TimeSpans in C#
I have a collection of objects that include a TimeSpan variable:我有一个包含 TimeSpan 变量的对象集合:
MyObject
{
TimeSpan TheDuration { get; set; }
}
I want to use LINQ to sum those times.我想用 LINQ 来总结那些时间。 Of course, (from r in MyCollection select r.TheDuration).Sum();当然,(来自 MyCollection 中的 r select r.TheDuration).Sum(); doesn't work!不起作用!
I'm thinking of changing the datatype of TheDuration to an int and then summing it and converting the sum to a TimeSpan.我正在考虑将 TheDuration 的数据类型更改为 int,然后对其求和并将总和转换为 TimeSpan。 That will be messy because each TheDuration in my collection is used in as a timespan somewhere else.这会很麻烦,因为我收藏中的每个 TheDuration 都在其他地方用作时间跨度。
Any suggestion on this summation?对这个总结有什么建议吗?
Unfortunately, there isn't a an overload of Sum
that accepts an IEnumerable<TimeSpan>
.不幸的是,没有接受IEnumerable<TimeSpan>
的Sum
重载。 Additionally, there's no current way of specifying operator-based generic constraints for type-parameters, so even though TimeSpan
is "natively" summable, that fact can't be picked up easily by generic code.此外,目前还没有为类型参数指定基于运算符的泛型约束的方法,因此即使TimeSpan
是“本机”可求和的,泛型代码也无法轻松获取这一事实。
One option would be to, as you say, sum up an integral-type equivalent to the timespan instead, and then turn that sum into a TimeSpan
again.一种选择是,如您所说,总结与时间跨度等效的整数类型,然后再次将该总和转换为时间TimeSpan
。 The ideal property for this is TimeSpan.Ticks
, which round-trips accurately.对此的理想属性是TimeSpan.Ticks
,它可以准确地往返。 But it's not necessary to change the property-type on your class at all;但是根本没有必要更改类的属性类型; you can just project :你可以只是项目:
var totalSpan = new TimeSpan(myCollection.Sum(r => r.TheDuration.Ticks));
Alternatively, if you want to stick to the TimeSpan's +
operator to do the summing, you can use the Aggregate
operator:或者,如果您想坚持使用 TimeSpan 的+
运算符进行求和,您可以使用Aggregate
运算符:
var totalSpan = myCollection.Aggregate
(TimeSpan.Zero,
(sumSoFar, nextMyObject) => sumSoFar + nextMyObject.TheDuration);
This works well (code based on Ani's answer)这很有效(基于 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 :用法 :
If Periods is a list of objects with a Duration property如果 Periods 是具有 Duration 属性的对象列表
TimeSpan total = Periods.Sum(s => s.Duration)
I believe this is the cleanest LINQ extension:我相信这是最干净的 LINQ 扩展:
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));
}
}
Usage is the same:用法是一样的:
TimeSpan total = Periods.Sum(s => s.Duration)
Here's what I tried and it worked:这是我尝试过的并且有效:
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; }
}
I put this in a class to add an extension method to a collection of 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);
}
}
So now, I can write TotalDuration = (my LINQ query that returns a collection of timespan).TotalTime();所以现在,我可以编写 TotalDuration = (我的 LINQ 查询返回时间跨度集合).TotalTime();
Voila!瞧!
I created extension method for IEnumerable<TimeSpan>我为 IEnumerable<TimeSpan> 创建了扩展方法
public static class TimeSpanExtensions
{
public static TimeSpan Sum(this IEnumerable<TimeSpan> source)
{
return source.Aggregate(TimeSpan.Zero, (subtotal, time) => subtotal.Add(time));
}
}
Thanks to that I can use:多亏了我可以使用:
IEnumerable<TimeSpan> times = new List<TimeSpan>();
TimeSpan totalTime = times.Sum();
This works for both a collection, and a property within a 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));
}
}
You can use .Aggregate
rather than .Sum
, and pass it a timespan-summing function that you write, like this:您可以使用.Aggregate
而不是.Sum
,并将您编写的时间跨度求和函数传递给它,如下所示:
TimeSpan AddTimeSpans(TimeSpan a, TimeSpan b)
{
return a + b;
}
Once you understand that timespans can't be summed and know to use Ticks
, it seems to me that this extension to just convert a long into a timespan looks more linq-ee .一旦你理解了时间跨度不能相加并知道使用Ticks
,在我看来,这个只是将 long 转换为时间跨度的扩展看起来更linq-ee 。 I believe it lets the reader have a more readable view of the operation:我相信它可以让读者对操作有一个更易读的视图:
var times = new[] { new TimeSpan(0, 10, 0), new TimeSpan(0, 20, 0), new TimeSpan(0, 30, 0) };
times.Sum(p => p.Ticks)
.ToTimeSpan(); // output: 01:00:00
Here is the one extension:这是一个扩展:
public static class LongExtensions
{
public static TimeSpan ToTimeSpan(this long ticks)
=> new TimeSpan(ticks);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.