IEnumerable is empty?

.NetLinqIenumerable

.Net Problem Overview


I know it probably doesnt matter/affect performance for the most part but I hate the idea of getting an IEnumerable and doing .Count(). Is there a IsEmpty or NotEmpty or some function? (similar to stl empty())

.Net Solutions


Solution 1 - .Net

You want IEnumerable.Any() extension method (.Net Framework 3.5 and above). It avoids counting over the elements.

Solution 2 - .Net

if it is not generic than something like this

enumeration.Cast<object>().Any();

if it's generic, use extension of Enumerable as was said already

Solution 3 - .Net

Without any need of LINQ, you can do following:

bool IsEmpty(IEnumerable en)
{
    foreach(var c in en) { return false; }
    return true;
}

Solution 4 - .Net

You can use extension methods such as Any() or Count(). Count() is more costly than Any(), since it must execute the whole enumeration, as others have pointed out.

But in the case of lazy evaluation (e.g. a method that uses yield), either can be costly. For example, with the following IEnumerable implementation, each call to Any or Count will incur the cost of a new roundtrip to the database:

IEnumerable<MyObject> GetMyObjects(...)
{
    using(IDbConnection connection = ...)
    {
         using(IDataReader reader = ...)
         {
             while(reader.Read())
             {
                 yield return GetMyObjectFromReader(reader);
             }
         }
    }
}

I think the moral is:

  • If you only have an IEnumerable<T>, and you want to do more than just enumerate it (e.g. use Count or Any), then consider first converting it to a List (extension method ToList). In this way you guarantee to only enumerate once.

  • If you are designing an API that returns a collection, consider returning ICollection<T> (or even IList<T>) rather than IEnumerable<T> as many people seem to recommend. By doing so you are strengthening your contract to guarantee no lazy evaluation (and therefore no multiple evaluation).

Please note I am saying you should consider returning a collection, not always return a collection. As always there are trade-offs, as can be seen from the comments below.

  • @KeithS thinks you should never yield on a DataReader, and while I never say never, I'd say it's generally sound advice that a Data Access Layer should return an ICollection<T> rather than a lazy-evaluated IEnumerable<T>, for the reasons KeithS gives in his comment.

  • @Bear Monkey notes that instantiating a List could be expensive in the above example if the database returns a large number of records. That's true too, and in some (probably rare) cases it may be appropriate to ignore @KeithS's advice and return a lazy-evaluated enumeration, provided the consumer is doing something that is not too time-consuming (e.g. generating some aggregate values).

Solution 5 - .Net

Keep in mind that IEnumerable is just an interface. The implementation behind it can be very different from class to class (consider Joe's example). The extension method IEnumerable.Any() has to be a generic approach and may not be what you want (performance wise). Yossarian suggests a means that should work for many classes, but if the underlying implementation does not use 'yield' you could still pay a price.

Generally, if you stick to collections or arrays wrapped in an IEnumerable interface, then Cristobalito and Yossarian probably have the best answers. My guess is the built-in .Any() ext method does what Yossarian recommends.

Solution 6 - .Net

On IEnumerable or IEnumerable<T>, no.

But it really doesn't make much sense. If a collection is empty and you try to iterate over it using IEnumerable, the call to IEnumerator.MoveNext() will simply return false at no performance cost.

Solution 7 - .Net

I don't think so, that's what Count is for. Besides, what will be faster:

  1. Accessing a Property and retrieving a stored Integer
  2. Accessing a Property and retrieving a stored Boolean

Solution 8 - .Net

You can also write your own Count extension method overloads like so:

    /// <summary>
    /// Count is at least the minimum specified.
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="min"></param>
    /// <returns></returns>
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        return source.Count(min, int.MaxValue);
    }

    /// <summary>
    /// Count is between the given min and max values
    /// </summary>
    /// <typeparam name="TSource"></typeparam>
    /// <param name="source"></param>
    /// <param name="min"></param>
    /// <param name="max"></param>
    /// <returns></returns>
    public static bool Count<TSource>(this IEnumerable<TSource> source, int min, int max)
    {
        if (source == null)
        {
            throw new ArgumentNullException("source");
        }
        if (min <= 0)
        {
            throw new ArgumentOutOfRangeException("min", "min must be a non-zero positive number");
        }
        if (max <= 0)
        {
            throw new ArgumentOutOfRangeException("max", "max must be a non-zero positive number");
        }
        if (min >= max)
            throw new ArgumentOutOfRangeException("min and max", "min must be lest than max");

        var isCollection = source as ICollection<TSource>;

        if (isCollection != null)
            return isCollection.Count >= min && isCollection.Count <= max;

        var count = 0;
        using (var enumerator = source.GetEnumerator())
        {
            while (enumerator.MoveNext())
            {
                count++;
                if (count >= min && count <= max)
                    return true;
            }
        }
        return false;
    }

Solution 9 - .Net

Use Enumerable.Empty() which rather than IEnumerable.Any() will prevent ending up having null list.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser34537View Question on Stackoverflow
Solution 1 - .NetcristobalitoView Answer on Stackoverflow
Solution 2 - .NetArtiomView Answer on Stackoverflow
Solution 3 - .NetnothrowView Answer on Stackoverflow
Solution 4 - .NetJoeView Answer on Stackoverflow
Solution 5 - .NetLesView Answer on Stackoverflow
Solution 6 - .NetJustin NiessnerView Answer on Stackoverflow
Solution 7 - .NetBobbyView Answer on Stackoverflow
Solution 8 - .NetJonathan ParkerView Answer on Stackoverflow
Solution 9 - .NetaliView Answer on Stackoverflow