I have a class A { public float Score; ... }
and an IEnumerable<A> items
and would like to find the A
which has minimal score.
Using items.Min(x => x.Score)
gives the minimal score and not the instance with minimal score.
How can I get the instance by iterating only once through my data?
Edit: So long there are three main solutions:
Writing an extension method (proposed by Svish). Pros: Easy to use and evaluates Score only once per item. Cons: Needs an extension method. (I choosed this solution for my application.)
Using Aggregate (proposed by Daniel Renshaw). Pros: Uses a built-in LINQ method. Cons: Slightly obfuscated to the untrained eye and calls evaluator more than once.
Implementing IComparable (proposed by cyberzed). Pros: Can use Linq.Min directly. Cons: Fixed to one comparer - can not freely choose comparer when performing the minimum computation.
Use Aggregate:
items.Aggregate((c, d) => c.Score < d.Score ? c : d)
As suggested, exact same line with more friendly names:
items.Aggregate((minItem, nextItem) => minItem.Score < nextItem.Score ? minItem : nextItem)
Try items.OrderBy(s => s.Score).FirstOrDefault();
Have a look at the MinBy extension method in MoreLINQ (created by Jon Skeet, now principally maintained by Atif Aziz).
MinBy documentation
MinBy source code (it's pretty straightforward and has no dependencies on other files).
This can be solved with a little simple iteration:
float minScore = float.MaxValue;
A minItem = null;
foreach(A item in items)
{
if(item.Score < minScore)
minItem = item;
}
return minItem;
It's not a nice LINQ query, but it does avoid a sorting operation and only iterates the list once, as per the question's requirements.
The quick way as I see it would be implementing IComparable for your class A (if possible ofcourse)
class A : IComparable<A>
It's a simple implementation where you write the CompareTo(A other)
have a look at IEnumerable(of T).Min on MSDN for a reference guide
A little fold should do:
var minItem = items.Aggregate((acc, c) => acc.Score < c.Score? acc : c);
Ah... too slow.
Jamie Penney got my vote. You can also say
items.OrderBy(s => s.Score).Take(1);
Which has the same effect. Use Take(5) to take the lowest 5 etc.
精彩评论