What is the best way to achieve "MinOrDefault" in Linq?

I am creating a list of decimal values ​​from a linq expression and I want a minimum non-zero value. However, it is possible that the linq expression will result in empty lists.

This will throw an exception and there will be no MinOrDefault to handle this situation.

decimal result = (from Item itm in itemList where itm.Amount > 0 select itm.Amount).Min(); 

What is the easiest way to set the result to 0 if the list is empty?

+75
c # linq
Jan 29 '10 at 22:22
source share
4 answers
 decimal? result = (from Item itm in itemList where itm.Amount != 0 select (decimal?)itm.Amount).Min(); 

Pay attention to conversion to decimal? . You will get an empty result if it is not (just handle it after the fact - I basically illustrate how to stop the exception). I also used the "non-zero" use != , Not > .

+51
Jan 29 '10 at 22:24
source share

What you want is:

 IEnumerable<double> results = ... your query ... double result = results.MinOrDefault(); 

Well, MinOrDefault() does not exist. But if we ourselves implemented this, 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, System.Linq has functionality that will produce the same result (somewhat differently):

 double result = results.DefaultIfEmpty().Min(); 

If the results sequence contains no elements, DefaultIfEmpty() will create a sequence containing one element - default(T) , which you can later call Min() .

If default(T) not what you want, then you can specify your own default:

 double myDefault = ... double result = results.DefaultIfEmpty(myDefault).Min(); 

Now, it's neat!

+119
Jan 30 '10 at 9:53
source share

The simplest in terms of simple execution of this code in a small amount of code, 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 get Min of what is the most accurate if we want to discover this empty condition.

If you really want to provide 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 the full set of MinOrDefault , whether you enable the selector or whether you specify the default.

From now on, your code is simple:

 decimal result = (from Item itm in itemList where itm.Amount > 0 select itm.Amount).MinOrDefault(); 

So, although it is not so neat to start from this moment, it is neat from this moment.

But wait! There are more!

Suppose you are using EF and want to use async support. Easy 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 am not using await , we can directly create a Task<TSource> that does what we need without it, and therefore avoid await hidden complications).

But wait, there still! Let's say we use this with IEnumerable<T> several times. Our approach is suboptimal. Of course we can do better!

First, Min defined in int? long? float? double? and decimal? already do what we want, anyway (as using the answer of Mark Gravel). Similarly, we also get the behavior that we want from the already specified t23, if it is called for any other T? . Therefore, let's make some small and, therefore, easily built-in 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, start over with a more general case:

 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 use this are:

 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 are really optimistic about performance, we can optimize for certain cases, just as 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 Min() set provided by Enumerable . This is the thing that uses T4 templates.

At the end of all this, we have about the same implementation of MinOrDefault() , as we might hope, for a wide range of types. Of course, not “neat” in the face of one use for it (again, just use DefaultIfEmpty().Min() ), but very “neat” if we use it a lot, so we have a good library that we can reuse use (or really, paste in StackOverflow answers ...).

+10
May 15 '15 at 12:02
source share

This approach will return the single smallest Amount value from itemList . Theoretically, this should avoid multiple database calls.

 decimal? result = (from Item itm in itemList where itm.Amount > 0) .Min(itm => (decimal?)itm.Amount); 

The null reference exception is no longer caused by the fact that we are using a type with a null value.

To avoid using methods such as Any before calling Min , we only have to make one trip to the database

0
May 15 '15 at 10:20
source share



All Articles