2016-11-02 9 views
15

Pytanie: biorąc pod uwagę IEnumerable<>, jak sprawdzić, która sekwencja zawiera więcej niż x elementów?Optymalizacja liczby LINQ()> X


MCVE:

static void Main(string[] args) 
{ 
    var test = Test().Where(o => o > 2 && o < 6); // ToList() 
    if (test.Count() > 1) // how to optimize this? 
     foreach (var t in test) // consumer 
      Console.WriteLine(t); 
} 

static IEnumerable<int> Test() 
{ 
    for (int i = 0; i < 10; i++) 
     yield return i; 
} 

Tutaj problemem jest to, co Count() będzie działał pełną sekwencję i to 1E6 + elementy (ToList() jest również zły pomysł). Nie mogę też zmienić kodu konsumenta (jest to metoda akceptująca kompletną sekwencję).

+1

Co o 'Any'? – Pikoh

+1

'Any()' powinno być dobrze na pewno? 'Count()' wylicza całą kolekcję w przeciwieństwie do 'Any', która określa, czy sekwencja zawiera jakiekolwiek elementy. – Ric

+0

@Pikoh, moje złe, 'Any()' działało sekwencyjnie aż do pierwszego elementu, który będzie pasował do warunków 'Where()'. Tak, 'Any()' zrobi dla przypadku gdy 'x = 1'. – Sinatr

Odpowiedz

18

W przypadku dużejtest kolekcji (gdy Count() jest drogie) można spróbować typowych trick:

if (test.Skip(1).Any()) 

W ogólnym przypadkutest.Count() > x może być zapisane w

if (test.Skip(x).Any()) 

Edytuj: ty może chcieć hide takiej sztuczki w metody, powiedzmy EnsureCount:

public static partial class EnumerableExtensions { 
    public static IEnumerable<T> EnsureCount<T>(this IEnumerable<T> source, int count) { 
     if (null == source) 
     throw new ArgumentNullException("source"); 

     if (count <= 0) 
     foreach (var item in source) 
      yield return item; 
     else { 
     List<T> buffer = new List<T>(count); 

     foreach (var item in source) { 
      if (buffer == null) 
      yield return item; 
      else { 
      buffer.Add(item); 

      if (buffer.Count >= count) { 
       foreach (var x in buffer) 
       yield return x; 

       buffer = null; 
      } 
      } 
     } 
     } 
    } 
    } 

i tak Twój kod będzie

var test = Test() 
    .Where(o => o > 2 && o < 6) 
    .EnsureCount(2); // <- Count() > 1, so at least 2 items 

    foreach (var t in test) 
    Console.WriteLine(t); 
+2

To jest bardzo sprytne! – n8wrl

+0

Jeśli zbiór ma 1 pozycję, to się nie uda ... '.Skip (-1)' zadziała, ale nie rozumiem, jak działa ta "sztuczka". – Phill

+0

@ Mountain: to oczekiwane zachowanie: kod próbuje zoptymalizować 'test.Count()> 1' proszę zauważyć'> 'nie'> = '. –