Sign in to follow this  
osmanb

[.net] LINQ to Objects (.Min)

Recommended Posts

osmanb    2082
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?

Share this post


Link to post
Share on other sites
mutex    1111
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);

Share this post


Link to post
Share on other sites
osmanb    2082
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++.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this