Dave New
Dave New

Reputation: 40002

Check if IEnumerable has ANY rows without enumerating over the entire list

I have the following method which returns an IEnumerable of type T. The implementation of the method is not important, apart from the yield return to lazy load the IEnumerable. This is necessary as the result could have millions of items.

public IEnumerable<T> Parse()
{
    foreach(...)
    {
        yield return parsedObject;
    }
}

Problem:

I have the following property which can be used to determine if the IEnumerable will have any items:

public bool HasItems
{
    get
    {
        return Parse().Take(1).SingleOrDefault() != null;
    }
}

Is there perhaps a better way to do this?

Upvotes: 25

Views: 34981

Answers (3)

Marco Merola
Marco Merola

Reputation: 879

The best way to answer this question, and to clear all doubts, is to see what the 'Any' function does.

   public static bool Any<TSource>(this IEnumerable<TSource> source) {
        if (source == null) throw Error.ArgumentNull("source");
        using (IEnumerator<TSource> e = source.GetEnumerator()) {
            if (e.MoveNext()) return true;
        }
        return false;
    }

https://github.com/microsoft/referencesource/blob/master/System.Core/System/Linq/Enumerable.cs

Upvotes: 6

drzaus
drzaus

Reputation: 24994

Similar to Howto: Count the items from a IEnumerable<T> without iterating? an Enumerable is meant to be a lazy, read-forward "list", and like quantum mechanics the act of investigating it alters its state.

See confirmation: https://dotnetfiddle.net/GPMVXH

    var sideeffect = 0;
    var enumerable = Enumerable.Range(1, 10).Select(i => {
        // show how many times it happens
        sideeffect++;
        return i;
    });

    // will 'enumerate' one item!
    if(enumerable.Any()) Console.WriteLine("There are items in the list; sideeffect={0}", sideeffect);

enumerable.Any() is the cleanest way to check if there are any items in the list. You could try casting to something not lazy, like if(null != (list = enumerable as ICollection<T>) && list.Any()) return true.

Or, your scenario may permit using an Enumerator and making a preliminary check before enumerating:

var e = enumerable.GetEnumerator();
// check first
if(!e.MoveNext()) return;
// do some stuff, then enumerate the list
do {
    actOn(e.Current);  // do stuff with the current item
} while(e.MoveNext()); // stop when we don't have anything else

Upvotes: 9

Marcus
Marcus

Reputation: 8659

IEnumerable.Any() will return true if there are any elements in the sequence and false if there are no elements in the sequence. This method will not iterate the entire sequence (only maximum one element) since it will return true if it makes it past the first element and false if it does not.

Upvotes: 49

Related Questions