Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I get the delta of two IEnumerables in LINQ?

Tags:

c#

linq

Not the aggregate delta but the delta of each element. Here is some code to explain what I mean:

var deltaTotals = _deltaEnumerable.Select(a => a.Amount).ToList();
var oldTotals = _totalsEnumerable.Select(d => d.Amount).ToList();

// trigger change in _totalsEnumerable

// ** can LINQ do the lines below 
var newTotals = totalsEnumerable.Select(d => d.Amount);
for (var i = 0; i < 7; i++) {
    var newAmount = oldTotals[i] - deltaTotals[i];
    Assert.That(newTotals.ElementAt(i), Is.EqualTo(newAmount));
}

It's the last four lines of code that seem like there might be a more elegant way to do in LINQ somehow.

Cheers,
Berryl

like image 862
Berryl Avatar asked Feb 28 '10 19:02

Berryl


2 Answers

What you want is the Enumerable.Zip extension method.

An example usage would be:

var delta = oldTotals.Zip(newTotals, (o, n) => n.Amount - o.Amount);

Note that this is new to .NET 4.0. In .NET 3.5 you would have to write your own extension. Something like this:

public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(
    this IEnumerable<TFirst> first,
    IEnumerable<TSecond> second,
    Func<TFirst, TSecond, TResult> resultSelector)
{
    using (var firstEnumerator = first.GetEnumerator())
    using (var secondEnumerator = second.GetEnumerator())
    {
        while ((firstEnumerator.MoveNext() && secondEnumerator.MoveNext()))
        {
            yield return resultSelector(firstEnumerator.Current,
                secondEnumerator.Current);
        }
    }
}
like image 182
Aaronaught Avatar answered Sep 20 '22 00:09

Aaronaught


As Aaronaught said in his answer, you should use the Zip method ; however, it's not available in .NET 3.5, only in 4.0. Here's a custom implementation :

    public static IEnumerable<TResult> Zip<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
    {
        if (first == null)
            throw new ArgumentNullException("first");
        if (second == null)
            throw new ArgumentNullException("second");
        if (selector == null)
            throw new ArgumentNullException("selector");

        return first.ZipIterator(second, selector);
    }

    private static IEnumerable<TResult> ZipIterator<TFirst, TSecond, TResult>(this IEnumerable<TFirst> first, IEnumerable<TSecond> second, Func<TFirst, TSecond, TResult> selector)
    {
        using (var enum1 = first.GetEnumerator())
        using (var enum2 = second.GetEnumerator())
        {
            while (enum1.MoveNext() && enum2.MoveNext())
            {
                yield return selector(enum1.Current, enum2.Current);
            }
        }
    }
like image 39
Thomas Levesque Avatar answered Sep 20 '22 00:09

Thomas Levesque