3

I have done a lot of searching to what appears to be a simple LINQ problem but I can't figure out how to do grab an object out of a collection that has a specified minimum (or max value) without resorting to a sort like this:

dim customers= GetCustomers()

dim youngest = (From c in customers
                 Order By c.age Ascending).ToList.First

This (untested code) structure works fine with the exception that the entire customer array must be sorted and placed into a list for the only purpose of extracting the first value. That can't be the best way to get the minimum!

Note that I want the whole c record in this case, not the minumum age of a customer that can be done like this (a typical example):

dim customers= GetCustomers()

dim youngest = (From c in customers
                 Select c.age).Min

Or even

dim customers= GetCustomers()

dim youngest = (From c in customers
                 Select c).Min(Function(x) x.age)

I can't for the life of me figure out how to get the whole object (or even the index) without resorting to the sort...

MPelletier
  • 16,256
  • 15
  • 86
  • 137
Hucker
  • 671
  • 1
  • 8
  • 25

3 Answers3

6

Again, C# code, I'm not sure of I got it right in VB.NET

C#

  Customer youngest = customers.Aggregate((c1, c2) => (c1.age < c2.age) ? c1 : c2);

VB.NET

  dim youngest = customers.Aggregate( Function(ByVal c1, ByVal c2) IF( (c1.age < c2.age) , c1 , c2  ) );
Sanjeevakumar Hiremath
  • 10,985
  • 3
  • 41
  • 46
1

There is no such operator in regular LINQ which will avoid sorting of the entire IEnumerable. But you're not the first one who needs a solution. For example check out Jason's answer here (though it's MaxBy and C# but you'll get an idea): Simple LINQ question in C#

Or MinBy from MoreLinq

Community
  • 1
  • 1
Snowbear
  • 16,924
  • 3
  • 43
  • 67
0

You are almost there. You should find that

dim youngest = (From c in customers
                Order By c.age Ascending
                Select c).First

does what you are looking for. (I am a C# guy, not a VB.NET guy, so my syntax may be off a bit.)

DocMax
  • 12,094
  • 7
  • 44
  • 44
  • That is basically what my first query does. Because you are doing the 'Order by c.age' there is an implied sort which adds an O(NlogN) performance hit vs the O(N) behavior I'm hoping to see with the Min function. – Hucker Mar 18 '11 at 23:27
  • I get that this has an implied sort. I had read your question to mean that you were trying to avoid the ToList, not that you were looking to avoid the sort altogether. Other answers give O(N) behavior, so there is no benefit to me updating mine. – DocMax Mar 19 '11 at 02:20
  • There's actually no reason to assume order by sorts the list first. The first and only call to MoveNext() could possibly just return the smallest item rather than sort the whole list and this would be O(N). This was not possible with the original version that had a ToList. In lazy eval languages like Haskell, it doesn't sort the list first and this would be O(N). Sadly, even in .NET 4.0, it seems it will QSORT first making people play games with LINQ rather than being declarative – Tony Lee Mar 20 '11 at 00:01