How can I efficiently determine if an IEnumerable has more than one element?
Given an initialised IEnumerable
:
IEnumerable<T> enumerable;
I would like to determine if it has more than one element. I think the most obvious way to do this is:
enumerable.Count() > 1
However, I believe Count()
enumerates the whole collection, which is unnecessary for this use case. For example, if the collection contains a very large amount of elements or provided its data from an external source, this could be quite wasteful in terms of performance.
How can I do this without enumerating any more than 2 elements?
Solution 1:
You can test this in many ways by combining the extension methods in System.Linq... Two simple examples are below:
bool twoOrMore = enumerable.Skip(1).Any();
bool twoOrMoreOther = enumerable.Take(2).Count() == 2;
I prefer the first one since a common way to check whether Count() >= 1
is with Any()
and therefore I find it more readable.
Solution 2:
For the fun of it, call Next() twice, then get another IEnumerable.
Or, write a small wrapper class for this specific goal: EnumerablePrefetcher : IEnumerable<T>
to try and fetch the specified amount of items upon initialization.
Its IEnumerable<T> GetItems()
method should use yield return in this fashion
foreach (T item in prefetchedItems) // array of T, prefetched and decided if IEnumerable has at least n elements
{
yield return item;
}
foreach (T item in otherItems) // IEnumerable<T>
{
yield return item;
}
Solution 3:
@Cameron-S's solution is simpler but below is more efficient. I came up with this based on Enumerable.Count()
method. Skip()
will always iterate and not short-circuit to get source
's count for ICollection
or ICollection<T>
type.
/// <summary>
/// Returns true if source has at least <paramref name="count"/> elements efficiently.
/// </summary>
/// <remarks>Based on int Enumerable.Count() method.</remarks>
public static bool HasCountOfAtLeast<TSource>(this IEnumerable<TSource> source, int count)
{
source.ThrowIfArgumentNull("source");
var collection = source as ICollection<TSource>;
if (collection != null)
{
return collection.Count >= count;
}
var collection2 = source as ICollection;
if (collection2 != null)
{
return collection2.Count >= count;
}
int num = 0;
checked
{
using (var enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
if (num >= count)
{
return true;
}
}
}
}
// returns true for source with 0 elements and count 0
return num == count;
}