views:

275

answers:

3

Given the following structure...

class Foo {

  public string Category { get; set; }
  public string Code { get; set; }
  public int Quantity { get; set; }

}

I am trying to use Linq to summarise a list of these objects by Category and Code, so that if I provide the following source...

List<Foo> foos = new List<Foo>() {
  new Foo {Category = @"A", Code = @"B", Quantity = 1},
  new Foo {Category = @"A", Code = @"B", Quantity = 2},
  new Foo {Category = @"C", Code = @"D", Quantity = 3},
  new Foo {Category = @"C", Code = @"D", Quantity = 4}
};

I end up with a list containing...

  Foo {Category = @"A", Code = @"B", Quantity = 3},
  Foo {Category = @"C", Code = @"D", Quantity = 7}

(where Quantity is the sum of the original quantities of matching objects).

I know I need to use a combination of the group by clause and the Sum() extension methods, I just do not seem to be able to find that correct combination.

Please note, there is no database behind this list, I am doing this purely with objects so extracting the original list including the sum is not an option.

Thanks.

+1  A: 

http://stackoverflow.com/questions/1790280/linq-merging-results-in-rows/1790359#1790359 is very similar:-

        var result = foos
            .GroupBy( foo => new 
            { 
                foo.Category, 
                foo.Code 
            } )
            .Select( fooGroup => new Foo 
            { 
                Category = fooGroup.Key.Category, 
                Code = fooGroup.Key.Code, 
                Quantity = fooGroup.Sum( foo => foo.Quantity ) 
            } );
Ruben Bartelink
Indeed the link wa very similar and useful (+1). I was able to get to the answer using what was there and then verify it against Luke's answer below.
Martin Robins
Ruben Bartelink
Point taken on the above/below - must try and remember that!
Martin Robins
+1  A: 
var query = foos
            .GroupBy(x => new { x.Category, x.Code })
            .Select(g => new Foo
                {
                    Category = g.Key.Category,
                    Code = g.Key.Code,
                    Quantity = g.Sum(x => x.Quantity)
                });
LukeH
Excellent, I was just getting to this conclusion based upon Ruben's answer above, but your answer handed it to me on a plate.
Martin Robins
There's no need for a separate call to Select here. GroupBy does all you need.
Jon Skeet
@Jon: Yep, I realised that after I'd posted, but since you got there first I've upvoted your answer rather than updating this one.
LukeH
+9  A: 

You want to group by both the category and the code, so you'll want an anonymous type for that - then just sum the quantities. You can do this in a single call to GroupBy if you use the right overload:

var query = list.GroupBy(
    item => new { item.Category, item.Code },
    (key, group) => new Foo { Category = key.Category, 
                              Code = key.Code,
                              Quantity = group.Sum(x => x.Quantity) });

If you want to do this with a query expression, you can use:

var query = from item in list
            group item by new { item.Category. item.Code } into items
            select new Foo { Category = items.Key.Category, 
                             Code = items.Key.Code,
                             Quantity = items.Sum(x => x.Quantity) });
Jon Skeet
Nice, didnt know that one! And who would have thought one'd learn something from FGITWing a small LINQ question!
Ruben Bartelink
Upvoted for the sheer clarity of the answer along with the fact that you provided it in both syntaxes. Thank you.
Martin Robins