2010-10-19 12 views
17

¿Existe alguna forma de saber cuál es el siguiente elemento de la secuencia al iterar? Como ejemplo concreto, decir que tengo una lista de enteros, y quiero calcular la diferencia entre cada elemento y su sucesor, así que por ejemplo me gustaría ser capaz de escribirForma de Linq para obtener la diferencia por partes entre el elemento y el siguiente elemento en la lista

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

donde el resultado Quiero es una lista {2,5, -6,8}.

Obviamente, esto es trivial en un bucle for, pero ¿alguien puede pensar en una buena línea en Linq para hacer este trabajo?

+0

esta pregunta podría ayudarle a http://stackoverflow.com/questions/2680228/linq-next-item-in-list –

Respuesta

32

Si está utilizando .NET 4 Posteriormente, se podría Zip y Skip:

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

Si está utilizando una versión anterior del marco, y/o que quería un poco manera más eficiente de esta manera, entonces se podría crear un sencillo método de extensión:

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

Me encanta; es exactamente lo que habría respondido si hubiera sabido que existía una función Zip(). – KeithS

+0

@Keith O puede usarlo desde MoreLinq para .NET 3.5. –

+0

El único inconveniente menor de esta solución es que myList se enumerará dos veces. Sin embargo, se puede arreglar fácilmente con el método Memoize de Reactive Extensions para .NET (Rx). –

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

Suponiendo que la lista tiene al menos 2 elementos.

Cuestiones relacionadas