2010-02-28 9 views
8

Nie jest to łączna delta, ale delta każdego elementu. Oto kod, aby wyjaśnić, co mam na myśli:Czy mogę uzyskać deltę dwóch IEnumerables w 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)); 
} 

To ostatnie cztery linie kodu, które wydają się jakby tam może być bardziej elegancki sposób to zrobić w LINQ jakoś.

Cheers,
Berryl

Odpowiedz

7

To, czego chcesz, to metoda rozszerzenia Enumerable.Zip.

Przykładem użycia może być:

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

Należy pamiętać, że jest to nowy do .NET 4.0. W .NET 3.5 musiałbyś napisać własne rozszerzenie. Coś takiego:

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 nie wydaje się być najbardziej intencją, która ujawnia mi imię, ale przynajmniej jest krótka. Kiedy pierwszy raz zobaczyłem twoją odpowiedź, pomyślałem, że to jest pobieranie! Pozdrawiam – Berryl

+0

To prawda, że ​​nie jest to najbardziej rozpoznawalna nazwa na świecie. Myślę, że Eric Lippert ukuł termin: http://blogs.msdn.com/ericlippert/archive/2009/05/07/zip-me-up.aspx. Tak czy inaczej, jest to przydatne rozszerzenie. :) – Aaronaught

+2

nazwa pochodzi od języków funkcjonalnych. Nazywa się to tak, ponieważ działa jak zamek błyskawiczny. Domyślnie powinno to zrobić coś w stylu krotek, gdzie pierwszy element pochodzi z listy 1, drugi z 2. – flq

5

Jak Aaronaught powiedział w swojej odpowiedzi, należy użyć metody Zip; jednak nie jest dostępny w .NET 3.5, tylko w wersji 4.0. Oto niestandardowa implementacja:

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

Słodki. Czy istnieje więcej rozszerzeń, takich jak Zip w wersji 4.0? – Berryl

+0

Zip jest jedyną nową metodą w klasie Enumerable, ale mogą być gdzieś inne ... –

Powiązane problemy