Search code examples
c#performancelinqienumerable

How do I check efficiently if IEnumerable has a single element?


Count() scans through all element, hence if (list.Count() == 1)will not perform well if enumerable contains a lot of elements.

Single() throws exception if there are not exactly one elements. Using try { list.Single(); } catch(InvalidOperationException e) {} is clumsy and inefficient.

SingleOrDefault() throws exception if there are more than one elements, hence if (list.SingleOrDefault() == null) (assuming TSource is of reference type) will not work for enumerables of size greater than one.


Solution

  • more direct:

    public static bool HasSingle<T>(this IEnumerable<T> sequence) {
        if (sequence is ICollection<T> list) return list.Count == 1; // simple case
        using(var iter = sequence.GetEnumerator()) {
            return iter.MoveNext() && !iter.MoveNext();
        }
    }
    

    Note, however, that you can only guarantee that you can read a sequence once, so in those cases: by the simple fact of checking that there is a single item, you can no longer get the item. So you might prefer something that gives you the value if there is one:

    public static bool HasSingle<T>(this IEnumerable<T> sequence, out T value)
    {
        if (sequence is IList<T> list)
        {
            if(list.Count == 1)
            {
                value = list[0];
                return true;
            }
        }
        else
        {
            using (var iter = sequence.GetEnumerator())
            {
                if (iter.MoveNext())
                {
                    value = iter.Current;
                    if (!iter.MoveNext()) return true;
                }
            }
        }
    
        value = default(T);
        return false;
    }