Related
I have a Person object with a Nullable DateOfBirth property. Is there a way to use LINQ to query a list of Person objects for the one with the earliest/smallest DateOfBirth value?
Here's what I started with:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
But all that does for me is to set firstBornDate to a DateTime value. What I'd like to get is the Person object that matches that. Do I need to write a second query like so:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
Or is there a leaner way of doing it?
People.Aggregate((curMin, x) => (curMin == null || (x.DateOfBirth ?? DateTime.MaxValue) <
curMin.DateOfBirth ? x : curMin))
Unfortunately there isn't a built-in method to do this, but it's easy enough to implement for yourself. Here are the guts of it:
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MinBy(selector, null);
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
comparer ??= Comparer<TKey>.Default;
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var min = sourceIterator.Current;
var minKey = selector(min);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0)
{
min = candidate;
minKey = candidateProjected;
}
}
return min;
}
}
Example usage:
var firstBorn = People.MinBy(p => p.DateOfBirth ?? DateTime.MaxValue);
Note that this will throw an exception if the sequence is empty, and will return the first element with the minimal value if there's more than one.
Alternatively, you can use the implementation we've got in MoreLINQ, in MinBy.cs. (There's a corresponding MaxBy, of course.)
Install via package manager console:
PM> Install-Package morelinq
NOTE: I include this answer for completeness since the OP didn't mention what the data source is and we shouldn't make any assumptions.
This query gives the correct answer, but could be slower since it might have to sort all the items in People, depending on what data structure People is:
var oldest = People.OrderBy(p => p.DateOfBirth ?? DateTime.MaxValue).First();
UPDATE: Actually I shouldn't call this solution "naive", but the user does need to know what he is querying against. This solution's "slowness" depends on the underlying data. If this is a array or List<T>, then LINQ to Objects has no choice but to sort the entire collection first before selecting the first item. In this case it will be slower than the other solution suggested. However, if this is a LINQ to SQL table and DateOfBirth is an indexed column, then SQL Server will use the index instead of sorting all the rows. Other custom IEnumerable<T> implementations could also make use of indexes (see i4o: Indexed LINQ, or the object database db4o) and make this solution faster than Aggregate() or MaxBy()/MinBy() which need to iterate the whole collection once. In fact, LINQ to Objects could have (in theory) made special cases in OrderBy() for sorted collections like SortedList<T>, but it doesn't, as far as I know.
People.OrderBy(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue)).First()
Would do the trick
So you are asking for ArgMin or ArgMax. C# doesn't have a built-in API for those.
I've been looking for a clean and efficient (O(n) in time) way to do this. And I think I found one:
The general form of this pattern is:
var min = data.Select(x => (key(x), x)).Min().Item2;
^ ^ ^
the sorting key | take the associated original item
Min by key(.)
Specially, using the example in original question:
For C# 7.0 and above that supports value tuple:
var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;
For C# version before 7.0, anonymous type can be used instead:
var youngest = people.Select(p => new {age = p.DateOfBirth, ppl = p}).Min().ppl;
They work because both value tuple and anonymous type have sensible default comparers: for (x1, y1) and (x2, y2), it first compares x1 vs x2, then y1 vs y2. That's why the built-in .Min can be used on those types.
And since both anonymous type and value tuple are value types, they should be both very efficient.
NOTE
In my above ArgMin implementations I assumed DateOfBirth to take type DateTime for simplicity and clarity. The original question asks to exclude those entries with null DateOfBirth field:
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
It can be achieved with a pre-filtering
people.Where(p => p.DateOfBirth.HasValue)
So it's immaterial to the question of implementing ArgMin or ArgMax.
NOTE 2
The above approach has a caveat that when there are two instances that have the same min value, then the Min() implementation will try to compare the instances as a tie-breaker. However, if the class of the instances does not implement IComparable, then a runtime error will be thrown:
At least one object must implement IComparable
Luckily, this can still be fixed rather cleanly. The idea is to associate a distanct "ID" with each entry that serves as the unambiguous tie-breaker. We can use an incremental ID for each entry. Still using the people age as example:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;
.NET 6 supports MaxBy/MinBy natively. So you will be able to do this with a simple
People.MinBy(p => p.DateOfBirth)
Solution with no extra packages:
var min = lst.OrderBy(i => i.StartDate).FirstOrDefault();
var max = lst.OrderBy(i => i.StartDate).LastOrDefault();
also you can wrap it into extension:
public static class LinqExtensions
{
public static T MinBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).FirstOrDefault();
}
public static T MaxBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).LastOrDefault();
}
}
and in this case:
var min = lst.MinBy(i => i.StartDate);
var max = lst.MaxBy(i => i.StartDate);
By the way... O(n^2) is not the best solution. Paul Betts gave fatster solution than my. But my is still LINQ solution and it's more simple and more short than other solutions here.
From .Net 6 (Preview 7) or later, there are new build-in method Enumerable.MaxBy and Enumerable.MinBy to achieve this.
var lastBorn = people.MaxBy(p => p.DateOfBirth);
var firstBorn = people.MinBy(p => p.DateOfBirth);
public class Foo {
public int bar;
public int stuff;
};
void Main()
{
List<Foo> fooList = new List<Foo>(){
new Foo(){bar=1,stuff=2},
new Foo(){bar=3,stuff=4},
new Foo(){bar=2,stuff=3}};
Foo result = fooList.Aggregate((u,v) => u.bar < v.bar ? u: v);
result.Dump();
}
Perfectly simple use of aggregate (equivalent to fold in other languages):
var firstBorn = People.Aggregate((min, x) => x.DateOfBirth < min.DateOfBirth ? x : min);
The only downside is that the property is accessed twice per sequence element, which might be expensive. That's hard to fix.
You can just do it like order by and limit/fetch only trick in SQL. So you order by DateOfBirth ascending and then just fetch first row.
var query = from person in People
where person.DateOfBirth!=null
orderby person.DateOfBirth
select person;
var firstBorn = query.Take(1).toList();
The following is the more generic solution. It essentially does the same thing (in O(N) order) but on any IEnumerable types and can mixed with types whose property selectors could return null.
public static class LinqExtensions
{
public static T MinBy<T>(this IEnumerable<T> source, Func<T, IComparable> selector)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (selector == null)
{
throw new ArgumentNullException(nameof(selector));
}
return source.Aggregate((min, cur) =>
{
if (min == null)
{
return cur;
}
var minComparer = selector(min);
if (minComparer == null)
{
return cur;
}
var curComparer = selector(cur);
if (curComparer == null)
{
return min;
}
return minComparer.CompareTo(curComparer) > 0 ? cur : min;
});
}
}
Tests:
var nullableInts = new int?[] {5, null, 1, 4, 0, 3, null, 1};
Assert.AreEqual(0, nullableInts.MinBy(i => i));//should pass
Try the following idea:
var firstBornDate = People.GroupBy(p => p.DateOfBirth).Min(g => g.Key).FirstOrDefault();
I was looking for something similar myself, preferably without using a library or sorting the entire list. My solution ended up similar to the question itself, just simplified a bit.
var min = People.Min(p => p.DateOfBirth);
var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == min);
EDIT again:
Sorry. Besides missing the nullable I was looking at the wrong function,
Min<(Of <(TSource, TResult>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TResult>)>)) does return the result type as you said.
I would say one possible solution is to implement IComparable and use Min<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>)), which really does return an element from the IEnumerable. Of course, that doesn't help you if you can't modify the element. I find MS's design a bit weird here.
Of course, you can always do a for loop if you need to, or use the MoreLINQ implementation Jon Skeet gave.
Another implementation, which could work with nullable selector keys, and for the collection of reference type returns null if no suitable elements found.
This could be helpful then processing database results for example.
public static class IEnumerableExtensions
{
/// <summary>
/// Returns the element with the maximum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the maximum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the maximum value of a selector function.</returns>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, 1);
/// <summary>
/// Returns the element with the minimum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the minimum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the minimum value of a selector function.</returns>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, -1);
private static TSource MaxOrMinBy<TSource, TKey>
(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, int sign)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
Comparer<TKey> comparer = Comparer<TKey>.Default;
TKey value = default(TKey);
TSource result = default(TSource);
bool hasValue = false;
foreach (TSource element in source)
{
TKey x = keySelector(element);
if (x != null)
{
if (!hasValue)
{
value = x;
result = element;
hasValue = true;
}
else if (sign * comparer.Compare(x, value) > 0)
{
value = x;
result = element;
}
}
}
if ((result != null) && !hasValue)
throw new InvalidOperationException("The source sequence is empty");
return result;
}
}
Example:
public class A
{
public int? a;
public A(int? a) { this.a = a; }
}
var b = a.MinBy(x => x.a);
var c = a.MaxBy(x => x.a);
IF you want to select object with minimum or maximum property value. another way is to use Implementing IComparable.
public struct Money : IComparable<Money>
{
public Money(decimal value) : this() { Value = value; }
public decimal Value { get; private set; }
public int CompareTo(Money other) { return Value.CompareTo(other.Value); }
}
Max Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Max();
Min Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Min();
In this way, you can compare any object and get the Max and Min while returning the object type.
Hope This will help someone.
A way via extension function on IEnumerable that returns both the object and the minimum found. It takes a Func that can do any operation on the object in the collection:
public static (double min, T obj) tMin<T>(this IEnumerable<T> ienum,
Func<T, double> aFunc)
{
var okNull = default(T);
if (okNull != null)
throw new ApplicationException("object passed to Min not nullable");
(double aMin, T okObj) best = (double.MaxValue, okNull);
foreach (T obj in ienum)
{
double q = aFunc(obj);
if (q < best.aMin)
best = (q, obj);
}
return (best);
}
Example where object is an Airport and we want to find closest Airport to a given (latitude, longitude). Airport has a dist(lat, lon) function.
(double okDist, Airport best) greatestPort = airPorts.tMin(x => x.dist(okLat, okLon));
You can use existing linq extension out there like MoreLinq. But if you just need only these methods, then you can use the simple code here:
public static IEnumerable<T> MinBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Min()];
}
public static IEnumerable<T> MaxBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Max()];
}
This is a simple way to get the minimum and maximum value:
`dbcontext.tableName.Select(x=>x.Feild1).Min()`
I have a Person object with a Nullable DateOfBirth property. Is there a way to use LINQ to query a list of Person objects for the one with the earliest/smallest DateOfBirth value?
Here's what I started with:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
But all that does for me is to set firstBornDate to a DateTime value. What I'd like to get is the Person object that matches that. Do I need to write a second query like so:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
Or is there a leaner way of doing it?
People.Aggregate((curMin, x) => (curMin == null || (x.DateOfBirth ?? DateTime.MaxValue) <
curMin.DateOfBirth ? x : curMin))
Unfortunately there isn't a built-in method to do this, but it's easy enough to implement for yourself. Here are the guts of it:
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MinBy(selector, null);
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
comparer ??= Comparer<TKey>.Default;
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var min = sourceIterator.Current;
var minKey = selector(min);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0)
{
min = candidate;
minKey = candidateProjected;
}
}
return min;
}
}
Example usage:
var firstBorn = People.MinBy(p => p.DateOfBirth ?? DateTime.MaxValue);
Note that this will throw an exception if the sequence is empty, and will return the first element with the minimal value if there's more than one.
Alternatively, you can use the implementation we've got in MoreLINQ, in MinBy.cs. (There's a corresponding MaxBy, of course.)
Install via package manager console:
PM> Install-Package morelinq
NOTE: I include this answer for completeness since the OP didn't mention what the data source is and we shouldn't make any assumptions.
This query gives the correct answer, but could be slower since it might have to sort all the items in People, depending on what data structure People is:
var oldest = People.OrderBy(p => p.DateOfBirth ?? DateTime.MaxValue).First();
UPDATE: Actually I shouldn't call this solution "naive", but the user does need to know what he is querying against. This solution's "slowness" depends on the underlying data. If this is a array or List<T>, then LINQ to Objects has no choice but to sort the entire collection first before selecting the first item. In this case it will be slower than the other solution suggested. However, if this is a LINQ to SQL table and DateOfBirth is an indexed column, then SQL Server will use the index instead of sorting all the rows. Other custom IEnumerable<T> implementations could also make use of indexes (see i4o: Indexed LINQ, or the object database db4o) and make this solution faster than Aggregate() or MaxBy()/MinBy() which need to iterate the whole collection once. In fact, LINQ to Objects could have (in theory) made special cases in OrderBy() for sorted collections like SortedList<T>, but it doesn't, as far as I know.
People.OrderBy(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue)).First()
Would do the trick
So you are asking for ArgMin or ArgMax. C# doesn't have a built-in API for those.
I've been looking for a clean and efficient (O(n) in time) way to do this. And I think I found one:
The general form of this pattern is:
var min = data.Select(x => (key(x), x)).Min().Item2;
^ ^ ^
the sorting key | take the associated original item
Min by key(.)
Specially, using the example in original question:
For C# 7.0 and above that supports value tuple:
var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;
For C# version before 7.0, anonymous type can be used instead:
var youngest = people.Select(p => new {age = p.DateOfBirth, ppl = p}).Min().ppl;
They work because both value tuple and anonymous type have sensible default comparers: for (x1, y1) and (x2, y2), it first compares x1 vs x2, then y1 vs y2. That's why the built-in .Min can be used on those types.
And since both anonymous type and value tuple are value types, they should be both very efficient.
NOTE
In my above ArgMin implementations I assumed DateOfBirth to take type DateTime for simplicity and clarity. The original question asks to exclude those entries with null DateOfBirth field:
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
It can be achieved with a pre-filtering
people.Where(p => p.DateOfBirth.HasValue)
So it's immaterial to the question of implementing ArgMin or ArgMax.
NOTE 2
The above approach has a caveat that when there are two instances that have the same min value, then the Min() implementation will try to compare the instances as a tie-breaker. However, if the class of the instances does not implement IComparable, then a runtime error will be thrown:
At least one object must implement IComparable
Luckily, this can still be fixed rather cleanly. The idea is to associate a distanct "ID" with each entry that serves as the unambiguous tie-breaker. We can use an incremental ID for each entry. Still using the people age as example:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;
.NET 6 supports MaxBy/MinBy natively. So you will be able to do this with a simple
People.MinBy(p => p.DateOfBirth)
Solution with no extra packages:
var min = lst.OrderBy(i => i.StartDate).FirstOrDefault();
var max = lst.OrderBy(i => i.StartDate).LastOrDefault();
also you can wrap it into extension:
public static class LinqExtensions
{
public static T MinBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).FirstOrDefault();
}
public static T MaxBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).LastOrDefault();
}
}
and in this case:
var min = lst.MinBy(i => i.StartDate);
var max = lst.MaxBy(i => i.StartDate);
By the way... O(n^2) is not the best solution. Paul Betts gave fatster solution than my. But my is still LINQ solution and it's more simple and more short than other solutions here.
From .Net 6 (Preview 7) or later, there are new build-in method Enumerable.MaxBy and Enumerable.MinBy to achieve this.
var lastBorn = people.MaxBy(p => p.DateOfBirth);
var firstBorn = people.MinBy(p => p.DateOfBirth);
public class Foo {
public int bar;
public int stuff;
};
void Main()
{
List<Foo> fooList = new List<Foo>(){
new Foo(){bar=1,stuff=2},
new Foo(){bar=3,stuff=4},
new Foo(){bar=2,stuff=3}};
Foo result = fooList.Aggregate((u,v) => u.bar < v.bar ? u: v);
result.Dump();
}
Perfectly simple use of aggregate (equivalent to fold in other languages):
var firstBorn = People.Aggregate((min, x) => x.DateOfBirth < min.DateOfBirth ? x : min);
The only downside is that the property is accessed twice per sequence element, which might be expensive. That's hard to fix.
You can just do it like order by and limit/fetch only trick in SQL. So you order by DateOfBirth ascending and then just fetch first row.
var query = from person in People
where person.DateOfBirth!=null
orderby person.DateOfBirth
select person;
var firstBorn = query.Take(1).toList();
The following is the more generic solution. It essentially does the same thing (in O(N) order) but on any IEnumerable types and can mixed with types whose property selectors could return null.
public static class LinqExtensions
{
public static T MinBy<T>(this IEnumerable<T> source, Func<T, IComparable> selector)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (selector == null)
{
throw new ArgumentNullException(nameof(selector));
}
return source.Aggregate((min, cur) =>
{
if (min == null)
{
return cur;
}
var minComparer = selector(min);
if (minComparer == null)
{
return cur;
}
var curComparer = selector(cur);
if (curComparer == null)
{
return min;
}
return minComparer.CompareTo(curComparer) > 0 ? cur : min;
});
}
}
Tests:
var nullableInts = new int?[] {5, null, 1, 4, 0, 3, null, 1};
Assert.AreEqual(0, nullableInts.MinBy(i => i));//should pass
Try the following idea:
var firstBornDate = People.GroupBy(p => p.DateOfBirth).Min(g => g.Key).FirstOrDefault();
I was looking for something similar myself, preferably without using a library or sorting the entire list. My solution ended up similar to the question itself, just simplified a bit.
var min = People.Min(p => p.DateOfBirth);
var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == min);
EDIT again:
Sorry. Besides missing the nullable I was looking at the wrong function,
Min<(Of <(TSource, TResult>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TResult>)>)) does return the result type as you said.
I would say one possible solution is to implement IComparable and use Min<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>)), which really does return an element from the IEnumerable. Of course, that doesn't help you if you can't modify the element. I find MS's design a bit weird here.
Of course, you can always do a for loop if you need to, or use the MoreLINQ implementation Jon Skeet gave.
Another implementation, which could work with nullable selector keys, and for the collection of reference type returns null if no suitable elements found.
This could be helpful then processing database results for example.
public static class IEnumerableExtensions
{
/// <summary>
/// Returns the element with the maximum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the maximum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the maximum value of a selector function.</returns>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, 1);
/// <summary>
/// Returns the element with the minimum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the minimum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the minimum value of a selector function.</returns>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, -1);
private static TSource MaxOrMinBy<TSource, TKey>
(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, int sign)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
Comparer<TKey> comparer = Comparer<TKey>.Default;
TKey value = default(TKey);
TSource result = default(TSource);
bool hasValue = false;
foreach (TSource element in source)
{
TKey x = keySelector(element);
if (x != null)
{
if (!hasValue)
{
value = x;
result = element;
hasValue = true;
}
else if (sign * comparer.Compare(x, value) > 0)
{
value = x;
result = element;
}
}
}
if ((result != null) && !hasValue)
throw new InvalidOperationException("The source sequence is empty");
return result;
}
}
Example:
public class A
{
public int? a;
public A(int? a) { this.a = a; }
}
var b = a.MinBy(x => x.a);
var c = a.MaxBy(x => x.a);
IF you want to select object with minimum or maximum property value. another way is to use Implementing IComparable.
public struct Money : IComparable<Money>
{
public Money(decimal value) : this() { Value = value; }
public decimal Value { get; private set; }
public int CompareTo(Money other) { return Value.CompareTo(other.Value); }
}
Max Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Max();
Min Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Min();
In this way, you can compare any object and get the Max and Min while returning the object type.
Hope This will help someone.
A way via extension function on IEnumerable that returns both the object and the minimum found. It takes a Func that can do any operation on the object in the collection:
public static (double min, T obj) tMin<T>(this IEnumerable<T> ienum,
Func<T, double> aFunc)
{
var okNull = default(T);
if (okNull != null)
throw new ApplicationException("object passed to Min not nullable");
(double aMin, T okObj) best = (double.MaxValue, okNull);
foreach (T obj in ienum)
{
double q = aFunc(obj);
if (q < best.aMin)
best = (q, obj);
}
return (best);
}
Example where object is an Airport and we want to find closest Airport to a given (latitude, longitude). Airport has a dist(lat, lon) function.
(double okDist, Airport best) greatestPort = airPorts.tMin(x => x.dist(okLat, okLon));
You can use existing linq extension out there like MoreLinq. But if you just need only these methods, then you can use the simple code here:
public static IEnumerable<T> MinBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Min()];
}
public static IEnumerable<T> MaxBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Max()];
}
This is a simple way to get the minimum and maximum value:
`dbcontext.tableName.Select(x=>x.Feild1).Min()`
I need to check if a sequence has any items satisfying some condition but at the same time NOT all items satisfying the same condition.
For example, for a sequence of 10 items I want to have TRUE if the sequence has at least one that satisfy the condition but not all:
10 items satisfying, 0 items not, result is FALSE
0 items satisfying, 10 items not, result is FALSE
1 item satisfying, 9 items not, result is TRUE
9 items satisfying, 1 item not, result is TRUE
I know I could to this:
mySequence.Any (item => item.SomeStatus == SomeConst) && !mySequence.All (item => item.SomeStatus == SomeConst)
But this is not optimal.
Is there a better way?
You'll like this.
var anyButNotAll = mySequence
.Select(item => item.SomeStatus == SomeConst)
.Distinct()
.Take(2)
.Count() == 2;
The Take(2) stops it iterating over any more elements than it has to.
If your concern is iterating through all of the elements in a large collection, you're okay - Any and All will short-circuit as soon as possible.
The statement
mySequence.Any (item => item.SomeStatus == SomeConst)
will return true as soon as one element that meets the condition is found, and
!mySequence.All (item => item.SomeStatus == SomeConst)
will return true as soon as one element does not.
Since both conditions are mutually exclusive, one of the statements is guaranteed to return after the first element, and the other is guaranteed to return as soon as the first element is found.
As pointed out by others, this solution requires starting to iterate through the collection twice. If obtaining the collection is expensive (such as in database access) or iterating over the collection does not produce the same result every time, this is not a suitable solution.
You could define your own extension method. This version is more verbose, but still readable, and it only enumerates the IEnumerable once:
bool AnyButNotAll<ItemT>(this IEnumerable<ItemT> sequence, Func<ItemT, bool> predicate)
{
bool seenTrue = false;
bool seenFalse = false;
foreach (ItemT item in sequence)
{
bool predResult = predicate(item);
if (predResult)
seenTrue = true;
if (!predResult)
seenFalse = true;
if (seenTrue && seenFalse)
return true;
}
return false;
}
Much shorter, but enumerates the IEnumerable twice:
bool AnyButNotAll<ItemT>(this IEnumerable<ItemT> sequence, Func<ItemT, bool> predicate)
{
return sequence.Any(predicate) && !sequence.All(predicate);
}
That is likely a fairly optimal solution if the source is a database. This extension method might be better depending on your source (I think, I just threw it together -- likely many many errors, consider it more pseudo code). The benefit here is it only enumerates once and does a short-circuit as soon as it has read enough to determine the outcome:
static bool SomeButNotAll<TSource>(this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
using(var iter=source.GetEnumerator())
{
if (iter.MoveNext())
{
bool initialValue=predicate(iter.Current);
while (iter.MoveNext())
if (predicate(iter.Current)!=initialValue)
return true;
}
}
return false; /* All */
}
You could try this:
var result = mySequence.Select(item => item.SomeStatus == SomeConst)
.Distinct().Count() > 1 ? false : true;
Basically I select true or false for each value, get distinct to only get one of each, then count those.
You could put your predicate in a variable so that you don't have to repeat the predicate twice:
Func<MyItemType, bool> myPredicate = item => item.SomeStatus == SomeConst;
if (mySequence.Any(myPredicate) && !mySequence.All(myPredicate))
...
If you wanted to define this as a method, you could take then approach Linq takes in defining both IEnumerable<T> and IQueryable<T> extension methods. This allows for the optimal approach to be taken automatically:
public static bool SomeButNotAll<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
return source.
Select(predicate)
.Distinct()
.Take(2)
.Count() == 2;
}
public static bool SomeButNotAll<T>(this IEnumerable<T> source, Func<T, bool> predicate)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
using(var en = source.GetEnumerator())
if(en.MoveNext())
{
bool first = predicate(en.Current);
while(en.MoveNext())
if(predicate(en.Current) != first)
return true;
}
return false;
}
If you're using EntityFramework (or another provider that provides a CountAsync you can also provide an asynchronous version easily:
public static async Task<bool> SomeButNotAllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, CancellationToken cancel)
{
if(source == null)
throw new ArgumentNullException("source");
if(predicate == null)
throw new ArgumentNullException("predicate");
cancel.ThrowIfCancellationRequested();
return await source.
Select(predicate)
.Distinct()
.Take(2)
.CountAsync(cancel)
.ConfigureAwait(false) == 2;
}
public static Task<bool> SomeButNotAllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate)
{
return source.SomeButNotAllAsync(predicate, CancellationToken.None);
}
You can use the Aggregate method to do both things at once. I would suggest to use an anonymous type for the TAccumulate, containing both counters. After the aggregation you can read both values from the resulting anonymous type.
(I can't type an example, I'm on my phone)
See the documentation here: https://msdn.microsoft.com/en-us/library/vstudio/bb549218(v=vs.100).aspx
I'm writing an entry for an AI competition in C#, and I'm looking for a more elegant way to search for items. (I'm much more familiar with embedded C programming, but I prefer C# for an AI contest.)
The contest server is using dmcs to compile entries, which is .Net framework 4.0; I'm using Visual Studio Express 2013 for my testing.
I'm trying to search for an item in a list with the maximum value of a parameter that also meets a certain prerequisite. I don't want the maximum value, though, I want the item that has said maximum value.
Here's my original code that does what I want using a foreach loop:
List<Region> myList = new List<Region>();
// ...
// myList gets populated with elements
// ...
Region biggest = null;
int biggestSize = -1;
foreach (Region r in myList)
{
// We only want elements that are eligible for expansion
if (r.EligibleForExpansion())
{
if (r.Size > biggestSize)
{
biggest = r;
biggestSize = r.Size;
}
}
}
return biggest; // I want the biggest Region, not the Size of the biggest region.
I'm trying to find a more elegant way to do this so I don't have foreach loops all over my code. I tried this:
return myList.Max(delegate(Region r) { if (r.EligibleForExpansion()) return r.Size; else return -1; });
However, that returns the Size value of the largest region, not the largest Region itself (which is what I need).
I know that my foreach code will return null if no Region meets the requirement while the Max code will give -1 (or any Region that doesn't meet the requirement); I can deal with either way.
I don't think I can just make Region IComparable, though; I have many searches for Region objects, and I need to sort by different parameters at different times, so the comparison function would be different in different searches.
I could just wrap my foreach code in a static function and call that wherever I need to search, but it seems like there should be a more elegant way to do this in C#.
Use MaxBy from moreLINQ library:
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MaxBy(selector, Comparer<TKey>.Default);
}
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
if (comparer == null) throw new ArgumentNullException("comparer");
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var max = sourceIterator.Current;
var maxKey = selector(max);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, maxKey) > 0)
{
max = candidate;
maxKey = candidateProjected;
}
}
return max;
}
}
like that:
var item = myList.Where(x => x.EligibleForExpansion())
.MaxBy(x => x.Size);
How about this?
myList.Where(r => r.EligibleForExpansion).OrderBy(r => r.Size).LastOrDefault()
You can use Aggregate out of the box for this purpose:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region)null, (max, cur) => (max == null ? cur : cur.Size > max.Size ? cur : max));
If Region were a value type (which it isn't) you could wrap the initial value in a nullable, and get a null value for an empty list:
var item = myList
.Where(r => r.EligibleForExpansion())
.Aggregate((Region?)null, (max, cur) => (max == null ? cur : cur.Size > max.Value.Size ? cur : max));
I have a Person object with a Nullable DateOfBirth property. Is there a way to use LINQ to query a list of Person objects for the one with the earliest/smallest DateOfBirth value?
Here's what I started with:
var firstBornDate = People.Min(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue));
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
But all that does for me is to set firstBornDate to a DateTime value. What I'd like to get is the Person object that matches that. Do I need to write a second query like so:
var firstBorn = People.Single(p=> (p.DateOfBirth ?? DateTime.MaxValue) == firstBornDate);
Or is there a leaner way of doing it?
People.Aggregate((curMin, x) => (curMin == null || (x.DateOfBirth ?? DateTime.MaxValue) <
curMin.DateOfBirth ? x : curMin))
Unfortunately there isn't a built-in method to do this, but it's easy enough to implement for yourself. Here are the guts of it:
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector)
{
return source.MinBy(selector, null);
}
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> selector, IComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException("source");
if (selector == null) throw new ArgumentNullException("selector");
comparer ??= Comparer<TKey>.Default;
using (var sourceIterator = source.GetEnumerator())
{
if (!sourceIterator.MoveNext())
{
throw new InvalidOperationException("Sequence contains no elements");
}
var min = sourceIterator.Current;
var minKey = selector(min);
while (sourceIterator.MoveNext())
{
var candidate = sourceIterator.Current;
var candidateProjected = selector(candidate);
if (comparer.Compare(candidateProjected, minKey) < 0)
{
min = candidate;
minKey = candidateProjected;
}
}
return min;
}
}
Example usage:
var firstBorn = People.MinBy(p => p.DateOfBirth ?? DateTime.MaxValue);
Note that this will throw an exception if the sequence is empty, and will return the first element with the minimal value if there's more than one.
Alternatively, you can use the implementation we've got in MoreLINQ, in MinBy.cs. (There's a corresponding MaxBy, of course.)
Install via package manager console:
PM> Install-Package morelinq
NOTE: I include this answer for completeness since the OP didn't mention what the data source is and we shouldn't make any assumptions.
This query gives the correct answer, but could be slower since it might have to sort all the items in People, depending on what data structure People is:
var oldest = People.OrderBy(p => p.DateOfBirth ?? DateTime.MaxValue).First();
UPDATE: Actually I shouldn't call this solution "naive", but the user does need to know what he is querying against. This solution's "slowness" depends on the underlying data. If this is a array or List<T>, then LINQ to Objects has no choice but to sort the entire collection first before selecting the first item. In this case it will be slower than the other solution suggested. However, if this is a LINQ to SQL table and DateOfBirth is an indexed column, then SQL Server will use the index instead of sorting all the rows. Other custom IEnumerable<T> implementations could also make use of indexes (see i4o: Indexed LINQ, or the object database db4o) and make this solution faster than Aggregate() or MaxBy()/MinBy() which need to iterate the whole collection once. In fact, LINQ to Objects could have (in theory) made special cases in OrderBy() for sorted collections like SortedList<T>, but it doesn't, as far as I know.
People.OrderBy(p => p.DateOfBirth.GetValueOrDefault(DateTime.MaxValue)).First()
Would do the trick
So you are asking for ArgMin or ArgMax. C# doesn't have a built-in API for those.
I've been looking for a clean and efficient (O(n) in time) way to do this. And I think I found one:
The general form of this pattern is:
var min = data.Select(x => (key(x), x)).Min().Item2;
^ ^ ^
the sorting key | take the associated original item
Min by key(.)
Specially, using the example in original question:
For C# 7.0 and above that supports value tuple:
var youngest = people.Select(p => (p.DateOfBirth, p)).Min().Item2;
For C# version before 7.0, anonymous type can be used instead:
var youngest = people.Select(p => new {age = p.DateOfBirth, ppl = p}).Min().ppl;
They work because both value tuple and anonymous type have sensible default comparers: for (x1, y1) and (x2, y2), it first compares x1 vs x2, then y1 vs y2. That's why the built-in .Min can be used on those types.
And since both anonymous type and value tuple are value types, they should be both very efficient.
NOTE
In my above ArgMin implementations I assumed DateOfBirth to take type DateTime for simplicity and clarity. The original question asks to exclude those entries with null DateOfBirth field:
Null DateOfBirth values are set to DateTime.MaxValue in order to rule them out of the Min consideration (assuming at least one has a specified DOB).
It can be achieved with a pre-filtering
people.Where(p => p.DateOfBirth.HasValue)
So it's immaterial to the question of implementing ArgMin or ArgMax.
NOTE 2
The above approach has a caveat that when there are two instances that have the same min value, then the Min() implementation will try to compare the instances as a tie-breaker. However, if the class of the instances does not implement IComparable, then a runtime error will be thrown:
At least one object must implement IComparable
Luckily, this can still be fixed rather cleanly. The idea is to associate a distanct "ID" with each entry that serves as the unambiguous tie-breaker. We can use an incremental ID for each entry. Still using the people age as example:
var youngest = Enumerable.Range(0, int.MaxValue)
.Zip(people, (idx, ppl) => (ppl.DateOfBirth, idx, ppl)).Min().Item3;
.NET 6 supports MaxBy/MinBy natively. So you will be able to do this with a simple
People.MinBy(p => p.DateOfBirth)
Solution with no extra packages:
var min = lst.OrderBy(i => i.StartDate).FirstOrDefault();
var max = lst.OrderBy(i => i.StartDate).LastOrDefault();
also you can wrap it into extension:
public static class LinqExtensions
{
public static T MinBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).FirstOrDefault();
}
public static T MaxBy<T, TProp>(this IEnumerable<T> source, Func<T, TProp> propSelector)
{
return source.OrderBy(propSelector).LastOrDefault();
}
}
and in this case:
var min = lst.MinBy(i => i.StartDate);
var max = lst.MaxBy(i => i.StartDate);
By the way... O(n^2) is not the best solution. Paul Betts gave fatster solution than my. But my is still LINQ solution and it's more simple and more short than other solutions here.
From .Net 6 (Preview 7) or later, there are new build-in method Enumerable.MaxBy and Enumerable.MinBy to achieve this.
var lastBorn = people.MaxBy(p => p.DateOfBirth);
var firstBorn = people.MinBy(p => p.DateOfBirth);
public class Foo {
public int bar;
public int stuff;
};
void Main()
{
List<Foo> fooList = new List<Foo>(){
new Foo(){bar=1,stuff=2},
new Foo(){bar=3,stuff=4},
new Foo(){bar=2,stuff=3}};
Foo result = fooList.Aggregate((u,v) => u.bar < v.bar ? u: v);
result.Dump();
}
Perfectly simple use of aggregate (equivalent to fold in other languages):
var firstBorn = People.Aggregate((min, x) => x.DateOfBirth < min.DateOfBirth ? x : min);
The only downside is that the property is accessed twice per sequence element, which might be expensive. That's hard to fix.
You can just do it like order by and limit/fetch only trick in SQL. So you order by DateOfBirth ascending and then just fetch first row.
var query = from person in People
where person.DateOfBirth!=null
orderby person.DateOfBirth
select person;
var firstBorn = query.Take(1).toList();
The following is the more generic solution. It essentially does the same thing (in O(N) order) but on any IEnumerable types and can mixed with types whose property selectors could return null.
public static class LinqExtensions
{
public static T MinBy<T>(this IEnumerable<T> source, Func<T, IComparable> selector)
{
if (source == null)
{
throw new ArgumentNullException(nameof(source));
}
if (selector == null)
{
throw new ArgumentNullException(nameof(selector));
}
return source.Aggregate((min, cur) =>
{
if (min == null)
{
return cur;
}
var minComparer = selector(min);
if (minComparer == null)
{
return cur;
}
var curComparer = selector(cur);
if (curComparer == null)
{
return min;
}
return minComparer.CompareTo(curComparer) > 0 ? cur : min;
});
}
}
Tests:
var nullableInts = new int?[] {5, null, 1, 4, 0, 3, null, 1};
Assert.AreEqual(0, nullableInts.MinBy(i => i));//should pass
Try the following idea:
var firstBornDate = People.GroupBy(p => p.DateOfBirth).Min(g => g.Key).FirstOrDefault();
I was looking for something similar myself, preferably without using a library or sorting the entire list. My solution ended up similar to the question itself, just simplified a bit.
var min = People.Min(p => p.DateOfBirth);
var firstBorn = People.FirstOrDefault(p => p.DateOfBirth == min);
EDIT again:
Sorry. Besides missing the nullable I was looking at the wrong function,
Min<(Of <(TSource, TResult>)>)(IEnumerable<(Of <(TSource>)>), Func<(Of <(TSource, TResult>)>)) does return the result type as you said.
I would say one possible solution is to implement IComparable and use Min<(Of <(TSource>)>)(IEnumerable<(Of <(TSource>)>)), which really does return an element from the IEnumerable. Of course, that doesn't help you if you can't modify the element. I find MS's design a bit weird here.
Of course, you can always do a for loop if you need to, or use the MoreLINQ implementation Jon Skeet gave.
Another implementation, which could work with nullable selector keys, and for the collection of reference type returns null if no suitable elements found.
This could be helpful then processing database results for example.
public static class IEnumerableExtensions
{
/// <summary>
/// Returns the element with the maximum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the maximum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the maximum value of a selector function.</returns>
public static TSource MaxBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, 1);
/// <summary>
/// Returns the element with the minimum value of a selector function.
/// </summary>
/// <typeparam name="TSource">The type of the elements of source.</typeparam>
/// <typeparam name="TKey">The type of the key returned by keySelector.</typeparam>
/// <param name="source">An IEnumerable collection values to determine the element with the minimum value of.</param>
/// <param name="keySelector">A function to extract the key for each element.</param>
/// <exception cref="System.ArgumentNullException">source or keySelector is null.</exception>
/// <exception cref="System.InvalidOperationException">source contains no elements.</exception>
/// <returns>The element in source with the minimum value of a selector function.</returns>
public static TSource MinBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) => MaxOrMinBy(source, keySelector, -1);
private static TSource MaxOrMinBy<TSource, TKey>
(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, int sign)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
Comparer<TKey> comparer = Comparer<TKey>.Default;
TKey value = default(TKey);
TSource result = default(TSource);
bool hasValue = false;
foreach (TSource element in source)
{
TKey x = keySelector(element);
if (x != null)
{
if (!hasValue)
{
value = x;
result = element;
hasValue = true;
}
else if (sign * comparer.Compare(x, value) > 0)
{
value = x;
result = element;
}
}
}
if ((result != null) && !hasValue)
throw new InvalidOperationException("The source sequence is empty");
return result;
}
}
Example:
public class A
{
public int? a;
public A(int? a) { this.a = a; }
}
var b = a.MinBy(x => x.a);
var c = a.MaxBy(x => x.a);
IF you want to select object with minimum or maximum property value. another way is to use Implementing IComparable.
public struct Money : IComparable<Money>
{
public Money(decimal value) : this() { Value = value; }
public decimal Value { get; private set; }
public int CompareTo(Money other) { return Value.CompareTo(other.Value); }
}
Max Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Max();
Min Implementation will be.
var amounts = new List<Money> { new Money(20), new Money(10) };
Money maxAmount = amounts.Min();
In this way, you can compare any object and get the Max and Min while returning the object type.
Hope This will help someone.
A way via extension function on IEnumerable that returns both the object and the minimum found. It takes a Func that can do any operation on the object in the collection:
public static (double min, T obj) tMin<T>(this IEnumerable<T> ienum,
Func<T, double> aFunc)
{
var okNull = default(T);
if (okNull != null)
throw new ApplicationException("object passed to Min not nullable");
(double aMin, T okObj) best = (double.MaxValue, okNull);
foreach (T obj in ienum)
{
double q = aFunc(obj);
if (q < best.aMin)
best = (q, obj);
}
return (best);
}
Example where object is an Airport and we want to find closest Airport to a given (latitude, longitude). Airport has a dist(lat, lon) function.
(double okDist, Airport best) greatestPort = airPorts.tMin(x => x.dist(okLat, okLon));
You can use existing linq extension out there like MoreLinq. But if you just need only these methods, then you can use the simple code here:
public static IEnumerable<T> MinBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Min()];
}
public static IEnumerable<T> MaxBys<T>(this IEnumerable<T> collection, Func<T, IComparable> selector)
{
var dict = collection.GroupBy(selector).ToDictionary(g => g.Key);
return dict[dict.Keys.Max()];
}
This is a simple way to get the minimum and maximum value:
`dbcontext.tableName.Select(x=>x.Feild1).Min()`