2010-10-19 8 views
17

Czy istnieje sposób Linq, aby wiedzieć, jaki jest następny element w sekwencji podczas iteracji? Jako konkretny przykład, że mam listę wskazówki i chcę, aby obliczyć różnicę między każdego elementu i jego następcy, więc na przykład chciałbym być w stanie napisaćSposób Linqa, aby uzyskać różnicę między poszczególnymi elementami i następny element na liście.

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

których wynik chcę to lista {2,5, -6,8}.

Oczywiście jest to banalne w pętli for, ale czy ktoś może pomyśleć o schludnym liniowcu w Linq, aby wykonać tę pracę?

+0

to pytanie może pomóc Ci http://stackoverflow.com/questions/2680228/linq-next-item-in-list –

Odpowiedz

32

Jeśli używasz .NET 4 następnie można Zip i Skip:

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

Jeśli korzystasz ze starszej wersji ram i/lub chciałeś nieznacznie bardziej efektywny sposób w ten sposób, to można utworzyć prostą metodę rozszerzenia:

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

Love it; to jest dokładnie to, co bym odpowiedział, gdybym wiedział, że istnieje funkcja Zip(). – KeithS

+0

@Keith Lub możesz go użyć z MoreLinq dla .NET 3.5. –

+0

Jedyną niewielką wadą tego rozwiązania jest to, że myList zostanie wyliczone dwukrotnie. Można je łatwo naprawić za pomocą metody Memoize z Reactive Extensions dla .NET (Rx). –

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

Zakładając, że lista zawiera co najmniej 2 elementy.

Powiązane problemy