Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A different take on FirstOrDefault

The IEnumerable extension method FirstOrDefault didn't exactly do as I wanted so I created FirstOrValue. Is this a good way to go about this or is there a better way?

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value)
{
    T first = source.FirstOrDefault(predicate);
    return Equals(first, default(T)) ? value : first;
}
like image 395
Aurequi Avatar asked Nov 10 '09 15:11

Aurequi


People also ask

What does FirstOrDefault mean?

FirstOrDefault: Returns the first element of a sequence, or a default value if no element is found. Throws exception: Only if the source is null. Use when: When more than 1 element is expected and you want only the first.

Which is faster SingleOrDefault or FirstOrDefault?

FirstOrDefault usually perform faster as compared SingleOrDefault, since these iterate the collection until they find the first match. While SingleOrDefault iterate the whole collection to find one single match.

What does FirstOrDefault mean in C#?

FirstOrDefault<TSource>(IEnumerable<TSource>, TSource) Returns the first element of a sequence, or a specified default value if the sequence contains no elements. FirstOrDefault<TSource>(IEnumerable<TSource>) Returns the first element of a sequence, or a default value if the sequence contains no elements.

What is difference between first and FirstOrDefault?

The major difference between First() and FirstOrDefault() is First will throw an exception when there are no results and FirstOrDefault won't. In fact, FirstOrDefault will simply return the null value (reference types) or the default value of the value type.


2 Answers

Your code is probably incorrect; you probably haven't considered all of the cases.

Of course, we cannot know if any code is correct or incorrect until we have a spec. So start by writing a one-line spec:

"FirstOrValue<T> takes a sequence of T, a predicate, and a value of T, and returns either the first item in the sequence that matches the predicate if there is one, or, if there is not, the stated value."

Does your attempt actually implement that spec? Certainly not! Test it:

int x = FirstOrValue<int>( new[] { -2, 0, 1 }, y=>y*y==y, -1);

this returns -1. The correct answer according to the spec is 0. The first item that matches the predicate is zero, so it should be returned.

A correct implementation of the spec would look like:

public static T FirstOrValue<T>(this IEnumerable<T> sequence, Func<T, bool> predicate, T value)
{
    if (sequence == null) throw new ArgumentNullException("sequence");
    if (predicate == null) throw new ArgumentNullException("predicate");
    foreach(T item in sequence)
        if (predicate(item)) return item;
    return value;
}

Always write a spec first, even if it's only a single sentence.

like image 183
Eric Lippert Avatar answered Oct 13 '22 16:10

Eric Lippert


default(T) will return null by default for reference types.

I would do this

public static T FirstOrValue<T>(this IEnumerable<T> source, Func<T, bool> predicate, T value)
{
    T first = source.FirstOrDefault(predicate);
    return first ?? value;
}
like image 38
Daniel A. White Avatar answered Oct 13 '22 16:10

Daniel A. White