tags:

views:

66

answers:

2

Is there a Linq way of knowing what the next element in the sequence is while iterating? As a concrete example, say I have a list of ints, and I want to calculate the difference between each element and its successor, so for example I would like to be able to write

var myList = new List<int>() { 1,3,8,2,10 };
var differences = myList.Select( ml => ml.Next() - ml ) // pseudo-code, obviously

where the result I want is a list { 2,5,-6,8 }.

Obviously this is trivial in a for loop, but can anyone think of a neat one-liner in Linq to do this job?

A: 
var differences = myList
    .Take(myList.Count - 1)
    .Select((v, i) => myList[i + 1] - v);

Assuming the list has at least 2 items.

Yuriy Faktorovich
+5  A: 

If you're using .NET 4 then you could Zip and Skip:

var differences = myList.Zip(myList.Skip(1), (x, y) => y - x);

If you're using an older version of the framework, and/or you wanted a slightly more efficient way of doing this, then you could create a simple extension method:

var differences = myList.Pairwise((x, y) => y - x);

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Pairwise<T>(
        this IEnumerable<T> source, Func<T, T, T> selector)
    {
        if (source == null) throw new ArgumentNullException("source");
        if (selector == null) throw new ArgumentNullException("selector");

        using (var e = source.GetEnumerator())
        {
            if (!e.MoveNext()) throw new InvalidOperationException("Sequence cannot be empty.");

            T prev = e.Current;

            if (!e.MoveNext()) throw new InvalidOperationException("Sequence must contain at least two elements.");

            do
            {
                yield return selector(prev, e.Current);
                prev = e.Current;
            } while (e.MoveNext());
        }
    }
}
LukeH
Love it; it's exactly what I would have answered had I known a Zip() function existed.
KeithS
@Keith Or you could use it from MoreLinq for .NET 3.5.
Yuriy Faktorovich
The only minor drawback of this solution is that myList will be enumerated twice. Can be easily fixed though with Memoize method from Reactive Extensions for .NET (Rx).
Ed'ka