LINQ doesn't have such an operator yet but LINQ-to-Objects operations use iterators, enumerators and IEnumerable anyway. What you ask is an operator that uses the current and previous item, like MoreLINQ's Pairwise operator. Such an operation would need only a single iteration to process pairs and produce output. Anything else will be a lot more expensive.
Using Pairwise you can write :
var avg=items.Pairwise((a, b) => (a - b))
.Average(ts=>ts.TotalMilliseconds);
Pairwise's code is simple and doesn't require multiple iterations :
public static IEnumerable<TResult> Pairwise<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TSource, TResult> resultSelector)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector));
return _(); IEnumerable<TResult> _()
{
using var e = source.GetEnumerator();
if (!e.MoveNext())
yield break;
var previous = e.Current;
while (e.MoveNext())
{
yield return resultSelector(previous, e.Current);
previous = e.Current;
}
}
}
A Pairwise method may appear in LINQ at some point. It's a very common operation in functional languages, and some MoreLINQ operators were added in .NET Core 6 Preview 4, like DistinctBy, MaxBy and more.
Using Aggregate
It's possible to use Aggregate to calculate an Average but it's very ugly and wasteful.
If you had a list of numbers you'd need to carry the item count and sum of items in the accumulator, and calculate the average in the end :
var nums=new[]{1.0,2.0,3.0};
var avg=nums.Aggregate(
(cnt:0.0,sum:0.0),
(acc,c)=>(cnt:acc.cnt+1,sum:acc.sum+c),
acc=>acc.sum/acc.cnt);
Console.WriteLine(avg);
In this case though you want to calculate the difference between the current item and the previous. This means you need to carry the previous value, the sum of differences, and add the difference between the current and previous value.
You also need to handle the first value, when there's no previous value. And since you calculate differences, the actual count is one less than the collection's count. Finally, if there are only two items you can't divide :
var nums=new[]{DateTime.Now,DateTime.Now.AddMinutes(1),DateTime.Now.AddMinutes(2)};
var avg=nums.Aggregate(
(cnt:0.0,sum:0.0,prev:DateTime.MinValue),
(acc,c)=>( cnt:acc.cnt+1,
sum:(acc.prev==DateTime.MinValue)
?0
:acc.sum+(c-acc.prev).TotalMilliseconds,
prev:c),
acc=>acc.cnt==1
?acc.sum
:acc.sum/(acc.cnt-1));
Console.WriteLine(avg);
This prints 60002.95775. It also took a lot of trial and error and NaN's to get it to work.