:)
var results =
from kvp in source
group kvp by kvp.Key.ToUpper() into g
select new
{
Group = g,
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
} into ag
from x in ag.Group //SelectMany
where x.Value != ag.Max
//for the update to the question - note: possibly ambiguous
let correct = ag.Group.Where(y => y.Value == ag.Max).First().Key
select new
{
Key = x.Key,
Max = ag.Max,
Total = ag.Total,
Correct = correct
};
I kinda like the question because of all the little parts (some are rarely used) that are required to make the answer.
Max = g.Max(kvp => kvp.Value),
Total = g.Sum(kvp => kvp.Value)
Performing multiple aggregations on a group is straightforward, yet challenging if you don't know how.
select a into b
This clause takes everything that happened before and starts a new query with the target. Without it, I'd have to start a new query like this:
var A = ... select a
var B = from b in A
It's important to note that the select into
clause removes kvp
and g
from scope.
from b in source
from a in b.A //SelectMany
This "unpacking" of the child collection turns my query about b's into a query about a's. Unlike the default Enumerable.SelectMany overload, it leaves the parent (b
) in scope.
where x.Value != ag.Max
Comparing a child's property with a parent's property? Delightful. It's important to remember to break out where
anytime you want to filter, even if you just grouped (there is no HAVING
).