What's the neatest way to achieve "MinOrDefault" in Linq?
What you want is this:
IEnumerable<double> results = ... your query ...
double result = results.MinOrDefault();
Well, MinOrDefault()
does not exist. But if we were to implement it ourselves it would look something like this:
public static class EnumerableExtensions
{
public static T MinOrDefault<T>(this IEnumerable<T> sequence)
{
if (sequence.Any())
{
return sequence.Min();
}
else
{
return default(T);
}
}
}
However, there is functionality in System.Linq
that will produce the same result (in a slightly different way):
double result = results.DefaultIfEmpty().Min();
If the results
sequence contains no elements, DefaultIfEmpty()
will produce a sequence containing one element - the default(T)
- which you subsequently can call Min()
on.
If the default(T)
is not what you want, then you could specify your own default with:
double myDefault = ...
double result = results.DefaultIfEmpty(myDefault).Min();
Now, that's neat!
decimal? result = (from Item itm in itemList
where itm.Amount != 0
select (decimal?)itm.Amount).Min();
Note the conversion to decimal?
. You'll get an empty result if there are none (just handle that after the fact - I'm mainly illustrating how to stop the exception). I also made "non-zero" use !=
rather than >
.
The neatest in terms of just doing it once in a small amount code is, as already mentioned:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).DefaultIfEmpty().Min();
With casting itm.Amount
to decimal?
and obtaining the Min
of that being the neatest if we want to be able to detect this empty condition.
If however you want to actually provide a MinOrDefault()
then we can of course start with:
public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).Min();
}
public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source)
{
return source.DefaultIfEmpty(defaultValue).Min();
}
public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).Min(selector);
}
public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
return source.DefaultIfEmpty().Min(selector);
}
You now have a full set of MinOrDefault
whether or not you include a selector, and whether or not you specify the default.
From this point on your code is simply:
decimal result = (from Item itm in itemList
where itm.Amount > 0
select itm.Amount).MinOrDefault();
So, while it's not as neat to begin with, it's neater from then on.
But wait! There's more!
Let's say you use EF and want to make use of the async
support. Easily done:
public static Task<TSource> MinOrDefaultAsync<TSource>(this IQueryable<TSource> source, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).MinAsync();
}
public static Task<TSource> MinOrDefaultAsync<TSource>(this IQueryable<TSource> source)
{
return source.DefaultIfEmpty(defaultValue).MinAsync();
}
public static Task<TSource> MinOrDefaultAsync<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TSource defaultValue)
{
return source.DefaultIfEmpty(defaultValue).MinAsync(selector);
}
public static Task<TSource> MinOrDefaultAsync<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector)
{
return source.DefaultIfEmpty().MinAsync(selector);
}
(Note that I don't use await
here; we can directly create a Task<TSource>
that does what we need without it, and hence avoid the hidden complications await
brings).
But wait, there's more! Let's say we're using this with IEnumerable<T>
some times. Our approach is sub-optimal. Surely we can do better!
First, the Min
defined on int?
, long?
, float?
double?
and decimal?
already do what we want anyway (as Marc Gravell's answer makes use of). Similarly, we also get the behaviour we want from the Min
already defined if called for any other T?
. So let's do some small, and hence easily inlined, methods to take advantage of this fact:
public static TSource? MinOrDefault<TSource>(this IEnumerable<TSource?> source, TSource? defaultValue) where TSource : struct
{
return source.Min() ?? defaultValue;
}
public static TSource? MinOrDefault<TSource>(this IEnumerable<TSource?> source) where TSource : struct
{
return source.Min();
}
public static TResult? Min<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult?> selector, TResult? defaultValue) where TResult : struct
{
return source.Min(selector) ?? defaultValue;
}
public static TResult? Min<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult?> selector) where TResult : struct
{
return source.Min(selector);
}
Now let's start with the more general case first:
public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue)
{
if(default(TSource) == null) //Nullable type. Min already copes with empty sequences
{
//Note that the jitter generally removes this code completely when `TSource` is not nullable.
var result = source.Min();
return result == null ? defaultValue : result;
}
else
{
//Note that the jitter generally removes this code completely when `TSource` is nullable.
var comparer = Comparer<TSource>.Default;
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
var currentMin = en.Current;
while(en.MoveNext())
{
var current = en.Current;
if(comparer.Compare(current, currentMin) < 0)
currentMin = current;
}
return currentMin;
}
}
return defaultValue;
}
Now the obvious overrides that make use of this:
public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source)
{
var defaultValue = default(TSource);
return defaultValue == null ? source.Min() : source.MinOrDefault(defaultValue);
}
public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, TResult defaultValue)
{
return source.Select(selector).MinOrDefault(defaultValue);
}
public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector)
{
return source.Select(selector).MinOrDefault();
}
If we're really bullish about performance, we can optimise for certain cases, just like Enumerable.Min()
does:
public static int MinOrDefault(this IEnumerable<int> source, int defaultValue)
{
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
var currentMin = en.Current;
while(en.MoveNext())
{
var current = en.Current;
if(current < currentMin)
currentMin = current;
}
return currentMin;
}
return defaultValue;
}
public static int MinOrDefault(this IEnumerable<int> source)
{
return source.MinOrDefault(0);
}
public static int MinOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector, int defaultValue)
{
return source.Select(selector).MinOrDefault(defaultValue);
}
public static int MinOrDefault<TSource>(this IEnumerable<TSource> source, Func<TSource, int> selector)
{
return source.Select(selector).MinOrDefault();
}
And so on for long
, float
, double
and decimal
to match the set of Min()
provided by Enumerable
. This is the sort of thing where T4 templates are useful.
At the end of all that, we have just about as performant an implementation of MinOrDefault()
as we could hope for, for a wide range of types. Certainly not "neat" in the face of one use for it (again, just use DefaultIfEmpty().Min()
), but very much "neat" if we find ourselves using it a lot, so we have a nice library we can reuse (or indeed, paste into answers on StackOverflow…).