Get Max and Min in a single LINQ query

There are new helpers for LINQ in .NET 6 for this matter: MaxBy and MinBy

var people = GetPeople();

var oldest = people.MaxBy(p => p.Age);
var youngest = people.MinBy(p => p.Age);

Console.WriteLine($"The oldest person is {oldest.Age}");
Console.WriteLine($"The youngest person is {youngest.Age}");

Min and Max are both aggregates. The general linq aggregate function is Aggregate

Assuming property A is an integer, and B is a string, you could write something like this:

objects.Aggregate(
    new {
        MinA = int.MaxValue,
        MaxB = string.Empty
    },
    (accumulator, o) => new {
        MinA = Math.Min(o.A, accumulator.MinA),
        MaxB = o.B > accumulator.MaxB ? o.B : accumulator.MaxB
    });

I know you wanted a Linq query, but I can't stop pointing out the non-linq version may be much more readable. Compare this:

IEnumerable<Message> messages = SomeMessages();

Func<DateTime, DateTime, DateTime> min = (dt1, dt2) => dt1 > dt2 ? dt2 : dt1;
Func<DateTime, DateTime, DateTime> max = (dt1, dt2) => dt1 > dt2 ? dt1 : dt2;

// linq version
var result = messages.Aggregate(
    new { StartDate = DateTime.MaxValue, EndDate = DateTime.MinValue }, /* initial value */
    (accumulate, current) => new { StartDate = min(accumulate.StartDate, current.ReceivedTime), EndDate = max(accumulate.EndDate, current.ReceivedTime) });

// non-linq version
DateTime start = DateTime.MaxValue;
DateTime end = DateTime.MinValue;
foreach (DateTime dt in messages.Select(msg => msg.ReceivedTime))
{
    start = min(start, dt);
    end = max(end, dt);
}

You can use Aggregate method.

var res = new { Min = objects[0].A, Max = objects[0].B }
var res = objects.Aggregate(res, (r, curr) => r.Min = r.Min < curr.A ? r.Min : curr.A; r.Max = r.Max > curr.B ? r.Max : curr.B);

Tags:

C#

Linq