Find average of collection of TimeSpans
Asked Answered
H

3

61

I have collection of TimeSpans, they represent time spent doing a task. Now I would like to find the average time spent on that task. It should be easy but for some reason I'm not getting the correct average.

Here's my code:

private TimeSpan? GetTimeSpanAverage(List<TimeSpan> sourceList)
{
    TimeSpan total = default(TimeSpan);

    var sortedDates = sourceList.OrderBy(x => x);

    foreach (var dateTime in sortedDates)
    {
        total += dateTime;
    }
    return TimeSpan.FromMilliseconds(total.TotalMilliseconds/sortedDates.Count());
}
Housekeeping answered 13/1, 2012 at 8:19 Comment(1)
Please provide sample data and what result you are getting and what result you are expecting. BTW: The ordering is not necessary.Almonte
A
109

You can use the Average overload that takes a collection of long in parameter:

double doubleAverageTicks = sourceList.Average(timeSpan => timeSpan.Ticks);
long longAverageTicks = Convert.ToInt64(doubleAverageTicks);

return new TimeSpan(longAverageTicks);
Abingdon answered 13/1, 2012 at 8:23 Comment(5)
This gives me a error: cannot convert from 'double' to 'long'Housekeeping
Instead of Ticks you can also use TotalSeconds and then use TimeSpan.FromSecondsWebbing
@Webbing Yes you can but you'll loose the sub second components which can be useful if your are timing short actionsAbingdon
@vc74 Well in that case you can have TotalMilliseconds, also in your case you are casting double to long so the loss is happening in this case too :)Webbing
@Webbing Yes but only once the average has been calculated which is better than for every timespan.Abingdon
D
12
var average = new TimeSpan(sourceList.Select(ts => ts.Ticks).Average());

Note, your method returns a Nullable, but doesn't need to, unless you want to return null if the source list is empty, in which case just do a separate check first.

Duarte answered 13/1, 2012 at 8:23 Comment(1)
There is no Enumerable.Average() overload that returns long value, as well as there is no TimeSpan constructor that accept double as the argument, so the example is not building for me. To make it compile I was forced to add an explicit cast (long) of the argument: var average = new TimeSpan((long)sourceList.Select(ts => ts.Ticks).Average());Wayfarer
W
4

In Addition to the above answer, I would suggest you take an average on the Seconds or MilliSeconds level (depending on what you require)

sourceList.Average(timeSpan => timeSpan.ToTalMilliseconds)

Now using this value you could arrive at the new TimeSpan using

TimeSpan avg = TimeSpan.FromMilliseconds(double value here)
Webbing answered 13/1, 2012 at 8:57 Comment(2)
I just want to warn everyone that if your average is not even one millisecond, than because of some precision problems your new average TimeSpan will be always 0.Zwick
@Zwick That was my exact problemCorody

© 2022 - 2024 — McMap. All rights reserved.