tags:

views:

118

answers:

2

I've got a table of transaction data for several locations and I need to find the sum of each location's maximum from a subset of locations. So imagine the following table:

location  year  transactions
  123     2009      57
  124     2009      23
  125     2009      45
  123     2010      64
  124     2010      12
  125     2010      66

So if I'm looking just for the data for locations 123 and 124 the code should pick out the value 64 for location 123 in 2010 and 23 for location 124 in 2009.

I've got the following code which works in that it finds the maximum value for each location and then adds it to the running total.

int total = 0;

foreach (var location in locationIds)
{
    int? temp = transactions.Where(t => t.Location == location)
                            .Max(t => t.Transactions);
    if (temp.HasValue)
    {
        total += temp.Value;
    }
}

Is there a more elegant way of coding this?

+8  A: 

I'm using the naming you used in the table in your example above. Judging from your code, you may need to change some of the names accordingly...but I can't be positive:

var locationsToInclude = new List<int> { 123, 124 };

var sum = transaction
    .Where(t => locationsToInclude.Contains(t.location))
    .GroupBy(t => t.location)
    .Sum(g => g.Max(t => t.transactions));
Justin Niessner
@Justin, +1, but I don't think you're `Select` clause needs to return the key **and** there is no "Value" property on `IGrouping`; just `.Select(x => x.Max(y => y.Number))` should suffice. (modifying the following clause to `.Sum(x => x)` of course.)
Kirk Woll
@Kirk - You're right. Updated.
Justin Niessner
Sorry for the inconsistency in the naming `number` == `Transactions`
ChrisF
This is nearly right, but there's no `Value` property on a grouping - it should just be `.Sum(grp => grp.Max(v => v.Number));`
Lee
+1: The edited version is the best way, although it's not a sequence of key-value pairs, its already an `IEnumerable<Transaction>`. Deleted my duplicate.
Ani
This works over all locations. I only want to get the values for a subset. That's my fault for not putting that in the question.
ChrisF
@ChrisF - Added the filtering via a Where clause.
Justin Niessner
Thanks - it was the `GroupBy` I was missing in my attempts!
ChrisF
+1: This is a great example of a LINQ query that is *actually* readable. I see so many of these questions asking to convert their traditional code into a LINQ statement and the LINQ solution ends up being *signficantly* harder to understand and read.
Brian Gideon
+2  A: 

This works over all locations. I only want to get the values for a subset.

Then how about this small modification to Justin's answer?

var sum = transaction
    .GroupBy(x => x.Location)
    .Where(g => locationIds.Contains(g.Key))
    .Sum(g => g.Max(x => x.Transactions));
Dan Tao
Depending on which LINQ provider he's using, wouldn't it be more efficient to filter using the Where clause prior to Grouping (I would assume grouping would be more intensive than filtering). A premature optimization to be sure...but a matter of curiosity never-the-less.
Justin Niessner
@Justin: Yeah, I would assume (pretty confidently) that you're right. I think your updated version makes the most sense.
Dan Tao