2012-06-30 9 views
9

Mam listę (uproszczony)LINQ: przyszłościowy stan

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

muszę {E, L} -> Produkty gdzie ich rodzaju == null i następny Kind == null zbyt

Załóżmy, że istnieje identyfikator, który zwiększa się i porządkuje.

Czy to możliwe w Linq?

Odpowiedz

9

Podoba Ci się to?

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: Kradzież rozwiązanie @Jeff Marcado do wdrożenia metodę rozszerzenia podobnego do powyższego zastosowania, ale trochę bardziej czytelne i nie czyniąc sobie z indeksu:

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

widzę indeks OUT w twoim rozwiązaniu wyjątek zakresu: jeśli ostatnią pozycją "Rodzaju" jest 'null', wówczas' list [i + 1] 'spowoduje nadindeksowanie listy. – nemesv

+0

Edytowane: Dobra rozmowa. – Ocelot20

+0

Nadal nie jest doskonały: zamień 'i' na' i + 1' w 'ElementAtOrDefault' oraz' ElementAt', aby był poprawny. – nemesv

5

dla podejścia funkcjonalnego, wy można wdrożyć wyliczający uprzedzoną tak:

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

moduł wyliczający będzie iterację krotek każdej pozycji i jest następujący element. Nie obejmuje to ostatniego elementu w kolekcji. To tylko kwestia wykonania zapytania.

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

Niestety będzie to bardzo nieefektywne. Wymieniasz długość kolekcji dwukrotnie i może to być bardzo kosztowne.

Lepiej byłoby napisać własny moduł wyliczający dla tego więc tylko przejść przez gromadzenie w jednym przebiegu:

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

Wtedy zapytania staje:

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

Cool. Mam nadzieję, że nie przeszkadza mi to, że ukradłem część ogólnego pomysłu, aby zaktualizować moją odpowiedź :) – Ocelot20