2016-11-02 2 views
15

Frage: gegeben IEnumerable<>, wie zu überprüfen, welche Sequenz enthält mehr als x Artikel?Optimize LINQ Count()> 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; 
} 

Das Problem hier ist, was Count() vollständige Sequenz ausgeführt wird, und das ist 1E6 + Artikel (ToList() ist auch keine gute Idee). Ich bin auch nicht berechtigt, den Verbrauchercode zu ändern (es ist eine Methode, die vollständige Abfolge akzeptiert).

+1

Was ist 'Any' sein? – Pikoh

+1

'Any()' sollte in Ordnung sein, sicher? 'Count()' zählt die gesamte Sammlung im Gegensatz zu 'Any' auf, die bestimmt, ob eine Sequenz irgendwelche Elemente enthält. – Ric

+0

@Pikoh, mein Schlechter, 'Any()' hat Sequenz bis zum ersten Element ausgeführt, was mit 'Where()' Bedingungen übereinstimmt. Ja, 'Any()' gilt für den Fall, wenn 'x = 1' ist. – Sinatr

Antwort

18

Bei großentest Sammlung (wenn Count()teuer ist) können Sie einen typischen Trick versuchen:

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

In allgemeinen Falltest.Count() > x kann in

if (test.Skip(x).Any()) 
neu geschrieben werden

Bearbeiten: Sie Möchten Sie folgendes verstecken Eine solche Tricks in einem Methode, sagen 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; 
      } 
      } 
     } 
     } 
    } 
    } 

und so wird Ihr Code

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

Das ist ziemlich schlau! – n8wrl

+0

Wenn die Sammlung 1 Element hat, tho das würde scheitern ... '.Skip (-1)' würde funktionieren, aber ich verstehe nicht, wie dieser 'Trick' funktioniert. – Phill

+0

@Phill: Es ist das erwartete Verhalten: der Code versucht, 'test.Count()> 1 'zu optimieren, bitte beachten Sie'> 'nicht'> = '. –