[.net] LINQ to Objects (.Min)

Started by
1 comment, last by osmanb 16 years ago
I've been using the recent C# and .NET stuff pretty heavily in a project for work. In particular, I've been using the LINQ and new lambda syntax quite a bit. I like it, because if I'm just selecting some subset of elements or doing any kind of query, I can almost always get away without temporary variables or a foreach loop. But today I hit a (minor) snag. I have a collection of things. I want to find the thing with the minimum timestamp. Previously, the surrounding code didn't need to know the object itself, so I was just doing this:

DateTime oldestTime = Collection.Min(obj=>obj.Timestamp);

Now I need to get the object itself. Obviously, I can easily write a foreach loop to find the oldest object. Or I could write a new extension method for IEnumerable and IQueryable to add to the huge set of operations provided by LINQ. But before I do that... am I missing some really obvious way to do this with the existing operations? Min always returns the value of the thing being compared. The only way I can see to implement this using the existing functions would be:

MyObject oldestObject = Collection.OrderBy(obj => obj.Timestamp).First();

This does what I want, but forces me to pay the price of an unnecessary sort on the collection. Before anyone asks, keeping the collection sorted by Timestamp isn't really an option ... it's queried in lots of different ways so I wouldn't really be solving anything by doing that. So, is there some other function that I've overlooked that would do what I want without the extra cost of sorting?
Advertisement
You can use the Aggregate operator:

class Person{    public string Name;    public int Age;}var people = new[]{    new Person { Name = "Bob", Age = 5 },    new Person { Name = "Joanne", Age = 19 },    new Person { Name = "Walter", Age = 25 },    new Person { Name = "Mary", Age = 1 },    new Person { Name = "Karen", Age = 13 },    new Person { Name = "Patricia", Age = 79 },    new Person { Name = "Billy", Age = 43 },};var youngestPerson = people.Aggregate((person, nextPerson) => person.Age < nextPerson.Age ? person : nextPerson);Console.WriteLine(youngestPerson.Name); // Prints Mary


The above is a bit more unweildy than Min, but you repackage it as an extension method.

public static TObject MinObject<TObject, TValue>(this IEnumerable<TObject> source, Func<TObject, TValue> selector){    var comparer = Comparer<TValue>.Default;    var minObject = source.Aggregate(        (obj, nextObject) =>        (comparer.Compare(selector(obj), selector(nextObject)) < 0) ? obj : nextObject        );    return minObject;}youngestPerson = people.MinObject(person => person.Age);

Ooh. Thanks. Yeah, I hadn't use Aggregate yet, so I wasn't familiar with the semantics. That looks perfect. I had already gone and written my own extension methods, but they're far more unwieldy than your version. rate++.

This topic is closed to new replies.

Advertisement