2012-06-30 10 views
9

tengo una lista (simplificado)LINQ: mirando hacia adelante condición

[Kind]  [Name] 
null  E 
null  W 
4   T 
5   G 
6   Q 
null  L 
null  V 
7   K 
2   Z 
0   F 

necesito {E, L} -> Elementos en su especie == null y el siguiente Kind == null demasiado

Supongamos que hay una ID que está aumentando y en orden.

¿Es esta visión de futuro posible en LINQ?

Respuesta

9

¿Te gusta?

void Main() 
{ 
    List<SomeClass> list = new List<SomeClass>() { 
     new SomeClass() { Kind = null, Name = "E" }, 
     new SomeClass() { Kind = null, Name = "W" }, 
     new SomeClass() { Kind = 4, Name = "T" }, 
     new SomeClass() { Kind = 5, Name = "G" }, 
     ... 
    }; 

    var query = list.Where ((s, i) => 
     !s.Kind.HasValue && 
     list.ElementAtOrDefault(i + 1) != null && 
     !list.ElementAt(i + 1).Kind.HasValue); 
} 

public class SomeClass 
{ 
    public int? Kind { get; set; } 
    public string Name { get; set; } 
} 

Editar: Robo de solución de @ Jeff Marcado para poner en práctica un método de extensión similar a la utilización anterior, pero un poco más limpio y no hacer a lidiar con el índice:

public static IEnumerable<TSource> WhereWithLookahead<TSource>(this IEnumerable<TSource> source, Func<TSource, TSource, bool> predicate) where TSource : class 
{ 
    using(var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
     { 
      //empty 
      yield break; 
     } 

     var current = enumerator.Current; 
     while (enumerator.MoveNext()) 
     { 
      var next = enumerator.Current; 

      if(predicate(current, next)) 
      { 
       yield return current; 
      } 

      current = next; 
     } 

     if (predicate(current, null)) 
     { 
      yield return current; 
     } 

    } 
} 

// Use: 
var query2 = list.WhereWithLookahead((current, next) => 
    !current.Kind.HasValue && 
    (next != null) && 
    next.Kind.HasValue); 
+2

veo un índice de excepción de rango en su solución: si el último elemento 'Tipo 'es' nulo', 'lista [i + 1]' sobreindexará la lista. – nemesv

+0

Editado: Buena llamada. – Ocelot20

+0

Todavía no es perfecto: reemplace 'i' con' i + 1' en 'ElementAtOrDefault' y' ElementAt' para hacerlo bien. – nemesv

5

Para un enfoque funcional, que puede implementar un empadronador búsqueda hacia delante, así:

IEnumerable<Item> collection = ...; 
var lookahead = collection.Zip(collection.Skip(1), Tuple.Create); 

El empadronador se repetirá a través de tuplas de cada elemento y es punto siguiente. Esto excluye el último elemento de la colección. Entonces solo se trata de realizar la consulta.

var query = collection.Zip(collection.Skip(1), Tuple.Create) 
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null) 
    .Select(tuple => tuple.Item1); 

Lamentablemente, esto será muy ineficiente. Está enumerando la longitud de la colección dos veces y puede ser muy costoso.

Sería mejor escribir su propio enumerador para este por lo que sólo va a través de la colección en una sola pasada:

public static IEnumerable<TResult> LookAhead<TSource, TResult>(
    this IEnumerable<TSource> source, 
    Func<TSource, TSource, TResult> selector) 
{ 
    if (source == null) throw new ArugmentNullException("source"); 
    if (selector == null) throw new ArugmentNullException("selector"); 

    using (var enumerator = source.GetEnumerator()) 
    { 
     if (!enumerator.MoveNext()) 
     { 
      //empty 
      yield break; 
     } 
     var current = enumerator.Current; 
     while (enumerator.MoveNext()) 
     { 
      var next = enumerator.Current; 
      yield return selector(current, next); 
      current = next; 
     } 
    } 
} 

Entonces la pregunta se convierte en:

var query = collection.LookAhead(Tuple.Create) 
    .Where(tuple => tuple.Item1.Kind == null && tuple.Item2.Kind == null) 
    .Select(tuple => tuple.Item1); 
+0

Cool. Espero que no te importe, robé parte de la idea general aquí para actualizar mi respuesta :) – Ocelot20

Cuestiones relacionadas