2012-06-30 6 views
9

Ich habe eine Liste (vereinfachte)Linq: vorausschauender Zustand

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

Ich brauche {E, L} -> Produkte, wo ihre Art == null und die nächste Art == null zu

Angenommen, es gibt eine ID, die sich erhöht und in der richtigen Reihenfolge befindet.

Ist das Vorwärtsschauen in Linq möglich?

Antwort

9

Gefällt mir?

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

Edit: Stehlen @ Jeff Marcado-Lösung eine Verlängerung Methode ähnlich den oben Gebrauch aber ein wenig sauberer zu implementieren und nicht, dass Sie mit dem Index beschäftigen:

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

Ich sehe einen Index aus Range Exception in Ihrer Lösung: Wenn das letzte Element 'Kind'' null' ist, dann wird 'list [i + 1]' die Liste überindizieren. – nemesv

+0

Bearbeitet: Guter Anruf. – Ocelot20

+0

Immer noch nicht perfekt: Ersetzen Sie "i" durch "i + 1" in "ElementAtOrDefault" und "ElementAt", um es richtig zu machen. – nemesv

5

Für einen funktionalen Ansatz, Sie kann wie so einen Look-Ahead-Enumerator implementieren:

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

der enumerator wird durch Tupel der einzelnen Elemente durchlaufen und es folgende Artikel. Dies schließt das letzte Element in der Sammlung aus. Dann ist es nur eine Frage der Abfrage.

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

Leider wird dies sehr ineffizient sein. Sie zählen die Länge der Sammlung zweimal auf und können sehr teuer sein.

Es wäre besser, Ihre eigenen Enumerator für diese zu schreiben, so dass Sie nur in einem Durchgang die Sammlung durchlaufen:

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

Dann wird die Abfrage wird:

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

Kühl. Ich hoffe, es macht Ihnen nichts aus, dass ich etwas von der allgemeinen Idee gestohlen habe, um meine Antwort zu aktualisieren :) – Ocelot20