我有一个 * 无序 * 的时间戳序列。我需要能够计算每个后续时间戳之间的 * 最小 、 最大 * 和 * 平均 * 差异。例如:
DateTimeOffset now = new DateTimeOffset(new DateTime(2022, 1, 1, 0, 0, 0, 0));
DateTimeOffset[] timestamps = new[] {
now,
now.AddSeconds(5),
now.AddSeconds(10),
now.AddSeconds(15),
now.AddSeconds(30),
now.AddSeconds(31)
};
IEnumerable<DateTimeOffset> timestampsSorted = timestamps.OrderByDescending(x => x);
应产生:
2022-01-01 00:00:31->2022-01-01 00:00:30 | 00:00:01
2022-01-01 00:00:30->2022-01-01 00:00:15 | 00:00:15
2022-01-01 00:00:15->2022-01-01 00:00:10 | 00:00:05
2022-01-01 00:00:10->2022-01-01 00:00:05 | 00:00:05
2022-01-01 00:00:05->2022-01-01 00:00:00 | 00:00:05
Min 00:00:01
Max 00:00:15
Avg 00:00:06.2000000
下面是我提出的过程性解决方案,如果我能使用LINQ简化它,那就太好了。
TimeSpan min = TimeSpan.MaxValue;
TimeSpan max = TimeSpan.MinValue;
List<TimeSpan> deltas = new();
for (int i = timestampsSorted.Length - 1; i > 0; i--)
{
DateTimeOffset later = timestamps[i];
DateTimeOffset prev = timestamps[i - 1];
TimeSpan delta = later - prev;
if (delta > max) { max = delta; }
if (delta < min) { min = delta; }
deltas.Add(delta);
Console.WriteLine($"{later:yyyy-MM-dd HH:mm:ss}->{prev:yyyy-MM-dd HH:mm:ss} | {delta}");
}
var result = new {
Min = min,
Max = max,
Avg = TimeSpan.FromMilliseconds(deltas.Average(d => d.TotalMilliseconds))
};
4条答案
按热度按时间xkftehaa1#
使用
LINQ
的内置Min
、Max
和Average
函数。请注意,对这些
Min
、Max
和Average
函数的单独调用会导致对data
数组中的项进行3次迭代。vybvopom2#
这远远不是最佳的,因为集合被枚举了几次,但是因为您要求...
参考:Zip()和Skip()
yeotifhr3#
First, the date difference with the next item is calculated and stored in the
diff
list:00:00:01
00:00:15
00:00:05
00:00:05
00:00:05
And then the
mean
,max
andaverage
are easily calculated:Min 00:00:01
Max 00:00:15
Avg 00:00:06.200
c8ib6hqw4#
You don't need to store all of the
delta
values in aList<TimeSpan>
on which to callAverage()
; it's more efficient to just keep a running sum and then divide it by the number of pairs compared (timestamps.Length - 1
). So this......would be changed to...
Aggregate()
is what you'd use to accumulate one or more values over the course of a sequence. Here's a method that usesAggregate()
to calculate the same values as yourfor
loop...The second parameter of this overload of
Aggregate()
is aFunc<>
that is passed the current element in the sequence (current
) and the state that was returned from the previous invocation of theFunc<>
(accumulator
). The first parameter provides the initial value ofaccumulator
. The third parameter is aFunc<>
that transforms the final value of this state to the return value ofAggregate()
. The state and return value are all value tuples .Note that
GetDeltaStatistics()
only needs anIEnumerable<DateTimeOffset>
and not aIList<DateTimeOffset>
orDateTimeOffset[]
; since there is no random access to adjacent elements, though, the value ofcurrent
is carried forward to the next invocation viaaccumulator.Previous
. I also made it the caller's responsibility to provide sorted input, but you could just as easily perform that inside the method.Calling
GetDeltaStatistics()
with......produces this output...
Whereas the original code would cause an exception to be thrown, for sequences with less than two elements the result has a
Count
of0
and the other fields arenull
.