VoidStar
VoidStar

Reputation: 5421

How can I get the first value of an IEnumerable iff the length is exactly one, or default otherwise?

I would like something like IEnumerable.FirstOrDefault, but I would like the default value returned if length != 1. Something like v.Count() == 1 ? v[0] : default. But I would prefer to do it without converting the entire enumerable into an array. What is the best way to get this behavior?

Upvotes: 2

Views: 1204

Answers (3)

MaKCbIMKo
MaKCbIMKo

Reputation: 2820

Have you tried to write a custom extension method, something like:

public static TSource MyFirstOrDefault<TSource>(this IEnumerable<TSource> source)
{
    return source.Count() == 1 ? source.First() : default(TSource);
}

but you need to keep in mind that methods Count() and First() will need to instantiate the inner collection of elements in order to get the count (or the first element).

Hope, that will help you.

Upvotes: 0

Michael Liu
Michael Liu

Reputation: 55389

Microsoft's implementation of Enumerable.FirstOrDefault works directly with the underlying enumerator. Yours can too:

public static T FirstIfOnlyOne<T>(this IEnumerable<T> source, T defaultValue = default(T))
{
    // (Null check and IList<T> optimization omitted...)

    using (IEnumerator<T> enumerator = source.GetEnumerator())
    {
        if (enumerator.MoveNext()) // Is there at least one item?
        {
            T item = enumerator.Current; // Save it.
            if (!enumerator.MoveNext()) // Is that it?
                return item;
        }
    }
    return defaultValue;
}

This is likely the most efficient implementation possible.

Upvotes: 9

Enigmativity
Enigmativity

Reputation: 117064

It is a good idea to avoid iterating the enumerable more than once as not all enumerables repeat the same values.

In order to ensure that your enumerable only returns 1 element it is the most efficient to take the first two elements from the enumerable and convert the result into an array. If the array then has two elements you know that the enumerable has more than one element without iterating the entire enumerable. It is possible for an enumerable to be infinitely long.

So here's how to do it:

public static T OneOnlyThenFirstOrDefault<T>(this IEnumerable<T> source)
{
    var beginning = source.Take(2).ToArray();
    return beginning.Length == 1 ? beginning[0] : default(T);
}

Upvotes: 7

Related Questions