2010-02-28 10 views
8

No es el delta agregado sino el delta de cada elemento. Aquí hay un código para explicar lo que quiero decir:¿Puedo obtener el delta de dos IEnumerables en LINQ?

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)); 
} 

Es las últimas cuatro líneas de código que parece como que podría haber una forma más elegante de hacerlo en LINQ alguna manera.

Cheers,
Berryl

Respuesta

7

Lo que desea es el método de extensión Enumerable.Zip.

Un ejemplo de uso sería:

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

Tenga en cuenta que esto es nuevo para .NET 4.0. En .NET 3.5, deberías escribir tu propia extensión. Algo como esto:

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); 
     } 
    } 
} 
+0

Cool. Zip no parece ser la forma más intencionada de revelar mi nombre, pero al menos es corto. Cuando vi tu respuesta por primera vez, pensé que era una descarga. Cheers – Berryl

+0

Cierto, no es el nombre más reconocible en el mundo. Creo que Eric Lippert acuñó el término: http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx. De cualquier manera, es una extensión útil. :) – Aaronaught

+2

el nombre proviene de los lenguajes funcionales. Se llama así porque funciona como una cremallera. Por defecto debería hacer algo como tuplas de forma, donde el primer elemento proviene de la lista 1, el 2do de 2. – flq

5

Como dijo Aaronaught en su respuesta, usted debe utilizar el método Zip; sin embargo, no está disponible en .NET 3.5, solo en 4.0. Aquí hay una implementación personalizada:

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); 
      } 
     } 
    } 
+0

Sweet. ¿Hay más extensiones como Zip en 4.0? – Berryl

+0

Zip es el único método nuevo en la clase Enumerable, pero puede haber otros en algún lugar ... –

Cuestiones relacionadas