Feedback

C# - LINQ Erweiterung: HasCount

Veröffentlicht von am 25.01.2014
(1 Bewertungen)
Wenn man bei Auflistungen die Count()-Methode verwendet, werden erst alle Elemente durch gegangen. Das ist sehr aufwendig, wenn man eine große Auflistung hat.
Die im Snippet gezeigten Methoden prüfen ob eine Mindestanzahl an Elementen vorhanden ist. D.h. dass das Zählen beim erreichen der Prüfzahl abgebrochen wird. Die 2. Überladung nimmt eine Auswahlfunktion entgegen.

Hinweis
Alternativen wie Array.Length sind oftmals schneller. Ggf. kann eine Messung der Ausführungszeit bei der Entscheidung helfen.

Benötigte Namespaces
System
System.Collections.Generic

MSDN Artikel
IEnumable<T>
http://msdn.microsoft.com/de-de/library/9eekhta0.aspx

Update
Wie in den Kommentaren von Xilefius vorgeschlagen braucht man diese Methoden nicht zwingend, folgende Aufrufe funktionieren genauso:

someList.Take(minCount).Count() == minCount

someList.Where(predicate).Take(minCount).Count() == minCount
/// <summary>
/// Überprüft ob die Auflistung eine Mindestzahl an Elementen aufweißt.
/// </summary>
/// <typeparam name="TSource">Der Typ der Elemente in der Auflistung.</typeparam>
/// <param name="list">Die Auflistung.</param>
/// <param name="count">Die Anzahl, bei der geprüft werden soll, ob mindestens so viele in <paramref name="list"/> enthalten sind.</param>
/// <returns><c>True</c>, wenn <paramref name="list"/> nicht <c>NULL</c> ist mindestens <paramref name="count"/> Elemente enthält. Andernfalls <c>False</c>.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">Wird ausgelöst, wenn <paramref name="count"/> kleiner als 0 ist.</exception>
public static bool HasCount<TSource>(this IEnumerable<TSource> list, int count)
{
    if (list == null)
        return false;
    if (count < 0)
        throw new ArgumentOutOfRangeException("count");
    if (count == 0)
        return true;

    using (var enumerator = list.GetEnumerator())
    {
        while (count > 0 && enumerator.MoveNext())
            --count;
    }
    if (count == 0)
        return true;
    return false;
}
/// <summary>
/// Überprüft ob die Auflistung eine Mindestzahl an Elementen aufweißt.
/// </summary>
/// <typeparam name="TSource">Der Typ der Elemente in der Auflistung.</typeparam>
/// <param name="list">Die Auflistung.</param>
/// <param name="count">Die Anzahl, bei der geprüft werden soll, ob mindestens so viele in <paramref name="list"/> enthalten sind.</param>
/// <param name="predicate">Eine Funktion, mit der geprüft wird, ob ein Element mit gezählt wird.</param>
/// <returns><c>True</c>, wenn <paramref name="list"/> nicht <c>NULL</c> ist mindestens <paramref name="count"/> Elemente enthält. Andernfalls <c>False</c>.</returns>
/// <exception cref="System.ArgumentOutOfRangeException">Wird ausgelöst, wenn <paramref name="count"/> kleiner als 0 ist.</exception>
/// <exception cref="System.ArgumentNullException">Wird ausgelöst, wenn <paramref name="predicate"/> nicht zugewiesen wurde.</exception>
public static bool HasCount<TSource>(this IEnumerable<TSource> list, int count, Func<TSource, bool> predicate)
{
    if (list == null)
        return false;
    if (count < 0)
        throw new ArgumentOutOfRangeException("count");
    if (predicate == null)
        throw new ArgumentNullException("predicate");
    if (count == 0)
        return true;

    using (var enumerator = list.GetEnumerator())
    {
        while (count > 0 && enumerator.MoveNext())
            if (predicate(enumerator.Current))
                --count;
    }
    if (count == 0)
        return true;
    return false;
}

6 Kommentare zum Snippet

DJ Doena schrieb am 22.05.2015:
Hier könnte man gleich noch eine zweite Methode schaffen, die auf der ersten aufbaut:


public static bool HasItems&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; list)
{
return list.HasCount(1);
}

public static bool HasCount&lt;TSource&gt;(this IEnumerable&lt;TSource&gt; list, Func&lt;TSource, bool&gt; predicate)
{
return list.HasCount(1, predicate);
}
DJ Doena schrieb am 22.05.2015:
Noch eins: deine Predicate-Variante lässt sich optimieren. Anstatt den predicate selbst zu prüfen, nimm einfach die Where-Funktion


var filtered = list.Where(predicate);
var hasCount = filtered.HasCount(count);
return hasCount;


Die absolute Aufrufmenge der Predicate-Funktion ist in beiden Varianten gleich.
Xilefius schrieb am 24.05.2015:
Man kann das auch ein einer Zeile mit den Standard .NET Linq Extensions schreiben, die effektiv auch nur soviele Einträge abzählt, wie benötigt

someList.Take(minCount).Count() == minCount

someList.Where(predicate).Take(minCount).Count() == minCount

das Take bricht ab und liefert keine weiteren Elemente, wenn minCount Elemente erreicht wurden
Xilefius schrieb am 24.05.2015:
Und die HasCount Methode gibt es auch bereits als "Any" Methode
Koopakiller schrieb am 24.05.2015:
@Xilefius Du hast vollkommen recht. Auf die Kombination .Take().Count() bin ich damals wohl nicht gekommen...
Auch was die Any-Methode betrifft kann ich nicht widersprechen.

Ich habe das entsprechend oben ergänzen.
DJ Doena schrieb am 24.05.2015:
Cool, bei Any hab ich bisher immer nur die Überladungsvariante benutzt, die prüft, ob Items in der Liste Bedingung X erfüllen. Nie auf die Idee gekommen, dass Any ohne defakto ein Count() > 0 ist.
 

Logge dich ein, um hier zu kommentieren!