1053

How can I do GroupBy multiple columns in LINQ

Something similar to this in SQL:

SELECT * FROM <TableName> GROUP BY <Column1>,<Column2>

How can I convert this to LINQ:

QuantityBreakdown
(
    MaterialID int,
    ProductID int,
    Quantity float
)

INSERT INTO @QuantityBreakdown (MaterialID, ProductID, Quantity)
SELECT MaterialID, ProductID, SUM(Quantity)
FROM @Transactions
GROUP BY MaterialID, ProductID
iliketocode
  • 6,652
  • 4
  • 41
  • 57
Sreedhar
  • 26,251
  • 31
  • 104
  • 163

14 Answers14

1282

Use an anonymous type.

Eg

group x by new { x.Column1, x.Column2 }
leppie
  • 109,129
  • 16
  • 185
  • 292
  • 34
    If you're new to grouping with anonymous types the use of the 'new' keyword in this example does the magic. – Chris Aug 06 '13 at 15:41
  • 8
    in case of mvc with nHibernate getting error for dll issues. Problem resolved by GroupBy(x=> new { x.Column1, x.Column2 }, (key, group) => new { Key1 = key.Column1, Key2 = key.Column2 , Result = group.ToList() }); – Milan Dec 07 '15 at 10:23
  • I thought in this case the new objects would by compared by reference, so no match - no groupping. – HoGo Jan 19 '16 at 07:57
  • 7
    @HoGo anonymous typed objects [implement their own Equals and GetHashCode](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/anonymous-types) methods which is used when grouping the objects. – Byron Carasco Sep 07 '17 at 18:57
  • A bit tough to visualize the output data structure when you're new to Linq. Does this create a grouping where the anonymous type is used as the key? – Jacques Oct 09 '17 at 15:04
  • @Jacques yes, but gets translated properly, eg in EF/Linq2Sql. – leppie Oct 09 '17 at 16:58
  • if you want a particular type (ie new Ttt { x.Column1, x.Column2 } ) use a struct to get equals as comparing the constituent parts - a class will by default compare the references which is probably not what you want since each instance will have a different reference – Simon Dowdeswell Dec 11 '17 at 00:56
  • I just want to know whether group by multiple column be affected to performance issue? I faced an issue after applied group by with 4 columns. Query returns around 5000 records which contains some duplicate values. – Damith Jan 24 '18 at 12:05
817

Procedural sample:

.GroupBy(x => new { x.Column1, x.Column2 })
iliketocode
  • 6,652
  • 4
  • 41
  • 57
Mo0gles
  • 9,447
  • 1
  • 19
  • 15
  • What type is the object returned? – mggSoft Feb 01 '13 at 16:22
  • 6
    @MGG_Soft that would be an anonymous type – Alex Mar 01 '13 at 14:15
  • This code is not working for me: "Invalid anonymous type declarator." – thalesfc Jul 07 '14 at 16:51
  • 19
    @Tom this should work the way it is. When you skip naming the fields of an anonymous type C# assumes you want to use the name of the finally accessed property/field from the projection. (So your example is equivalent to Mo0gles') – Chris Pfohl Jan 28 '15 at 13:54
  • @Crisfole yes I totall agree under most cases this is true. However there are times when the compiler can't infer the field names and they have to be explicitly specified. As in when you get the "Invalid anonymous type declarator" compile error. It's happened to me and also thalesfc hence the comment. – Tom Maher Jan 30 '15 at 09:38
  • @Tom. I think that only applies if you're calculating a value, e.g. new { x.Column1 + " " + x.Column2 }, as there will be no name to infer. Just using the property names, as in the sample above, should always work. – weenoid Feb 24 '15 at 15:41
  • if i'm returning this in my method, what should be the method return type? assume my entity is 'MyEntity' – Amir Chatrbahr Mar 16 '15 at 00:30
  • 1
    found my answer. I need to define a new entity (MyViewEntity) containing Column1 and Column2 properties and the return type is : IEnumerable> and Grouping code snip is : MyEntityList.GroupBy(myEntity => new MyViewEntity { Column1 = myEntity.Column1, Column2 = myEntity.Column2 }); – Amir Chatrbahr Mar 17 '15 at 01:05
  • @AmirChatrbahr It's an anonymous type, so there isn't an explicit type to define. That's why `var` was added to .NET. `var groupedStuff = stuff.GroupBy(x => new {x.Column1, x.Column2});` – Andy_Vulhop Apr 16 '15 at 19:36
  • @Andy_Vulhop let me clear it out. my problem was in DECLARING the method not calling the method and USING it. – Amir Chatrbahr Jun 26 '15 at 07:22
  • @AmirChatrbahr Ah, if you are returning the result of said grouping from a method, then yes, that makes sense. You can't define a method's return type as a anonymous type, so your solution works. Generally, I have pulled a group query and done some work or mapped to some model, so the use case of returning an `IEnumerable>` as you did hadn't occurred to me. – Andy_Vulhop Jun 29 '15 at 13:40
497

Ok got this as:

var query = (from t in Transactions
             group t by new {t.MaterialID, t.ProductID}
             into grp
                    select new
                    {
                        grp.Key.MaterialID,
                        grp.Key.ProductID,
                        Quantity = grp.Sum(t => t.Quantity)
                    }).ToList();
splattne
  • 100,048
  • 51
  • 202
  • 247
Sreedhar
  • 26,251
  • 31
  • 104
  • 163
  • 80
    +1 - Thanks for the comprehensive example. The other answer's snippets are too short and without context. Also you show an aggregate function (Sum in this case). Very helpful. I find the use of an aggregate function (i.e., MAX, MIN, SUM, etc.) side-by-side with grouping to be a common scenario. – barrypicker Jan 24 '14 at 18:44
  • Here : http://stackoverflow.com/questions/14189537/select-entire-duplicate-row-from-datatable-with-linq-c-sharp ,it is shown for a data table when grouping is based on a single column, whose name is known, but how can it be done if columns based on which the grouping is to be done has to be generated dynamically ? – b.g Dec 22 '16 at 12:42
  • This is really helpful in understanding the concept of grouping and applying aggregation over it. – rajibdotnet Sep 18 '17 at 22:48
  • 1
    Great example... just what i was looking for. I even needed the aggregate so this was the perfect answer even though I was looking for lambda i got enough from it to solve my needs. – Kevbo Aug 28 '18 at 21:00
  • having grp.Key. was what I needed to get it working, thanks! – rob23 Jan 14 '21 at 23:31
169

For Group By Multiple Columns, Try this instead...

GroupBy(x=> new { x.Column1, x.Column2 }, (key, group) => new 
{ 
  Key1 = key.Column1,
  Key2 = key.Column2,
  Result = group.ToList() 
});

Same way you can add Column3, Column4 etc.

Milan
  • 2,655
  • 1
  • 18
  • 25
  • 4
    That was very helpful and should get a lot more upvotes! `Result` contains all data sets linked to all columns. Thanks a lot! – j00hi Sep 15 '16 at 10:26
  • 1
    note: I had to use .AsEnumerable() instead of ToList() – GMan Sep 17 '16 at 15:19
  • 1
    Awesome, thanks for this. Here's my example. Note that GetFees returns an IQueryable RegistryAccountDA.GetFees(registryAccountId, fromDate, toDate) .GroupBy(x => new { x.AccountId, x.FeeName }, (key, group) => new { AccountId = key.AccountId, FeeName = key.FeeName, AppliedFee = group.Sum(x => x.AppliedFee) ?? 0M }).ToList(); – Craig B Sep 20 '16 at 02:27
  • 1
    Is it possbile to get other columns from this query, which were not grouped? If there is array of object, I would like to get this object grouped by two columns, but get all properties from the object, not just those two columns. – FrenkyB May 11 '18 at 10:43
43

Since C# 7 you can also use value tuples:

group x by (x.Column1, x.Column2)

or

.GroupBy(x => (x.Column1, x.Column2))
Nathan Tregillus
  • 5,248
  • 2
  • 43
  • 73
33

C# 7.1 or greater using Tuples and Inferred tuple element names (currently it works only with linq to objects and it is not supported when expression trees are required e.g. someIQueryable.GroupBy(...). Github issue):

// declarative query syntax
var result = 
    from x in inMemoryTable
    group x by (x.Column1, x.Column2) into g
    select (g.Key.Column1, g.Key.Column2, QuantitySum: g.Sum(x => x.Quantity));

// or method syntax
var result2 = inMemoryTable.GroupBy(x => (x.Column1, x.Column2))
    .Select(g => (g.Key.Column1, g.Key.Column2, QuantitySum: g.Sum(x => x.Quantity)));

C# 3 or greater using anonymous types:

// declarative query syntax
var result3 = 
    from x in table
    group x by new { x.Column1, x.Column2 } into g
    select new { g.Key.Column1, g.Key.Column2, QuantitySum = g.Sum(x => x.Quantity) };

// or method syntax
var result4 = table.GroupBy(x => new { x.Column1, x.Column2 })
    .Select(g => 
      new { g.Key.Column1, g.Key.Column2 , QuantitySum= g.Sum(x => x.Quantity) });
AlbertK
  • 7,861
  • 3
  • 29
  • 30
19

You can also use a Tuple<> for a strongly-typed grouping.

from grouping in list.GroupBy(x => new Tuple<string,string,string>(x.Person.LastName,x.Person.FirstName,x.Person.MiddleName))
select new SummaryItem
{
    LastName = grouping.Key.Item1,
    FirstName = grouping.Key.Item2,
    MiddleName = grouping.Key.Item3,
    DayCount = grouping.Count(), 
    AmountBilled = grouping.Sum(x => x.Rate),
}
Jay Bienvenu
  • 2,307
  • 4
  • 28
  • 37
8

Though this question is asking about group by class properties, if you want to group by multiple columns against a ADO object (like a DataTable), you have to assign your "new" items to variables:

EnumerableRowCollection<DataRow> ClientProfiles = CurrentProfiles.AsEnumerable()
                        .Where(x => CheckProfileTypes.Contains(x.Field<object>(ProfileTypeField).ToString()));
// do other stuff, then check for dups...
                    var Dups = ClientProfiles.AsParallel()
                        .GroupBy(x => new { InterfaceID = x.Field<object>(InterfaceField).ToString(), ProfileType = x.Field<object>(ProfileTypeField).ToString() })
                        .Where(z => z.Count() > 1)
                        .Select(z => z);
Chris Smith
  • 661
  • 7
  • 16
  • 1
    I couldn't do the Linq query "group c by new{c.Field("Title"),c.Field("CIF")}", and you saved me a lot of time!! the final query was: "group c by new{titulo= c.Field("Title"),cif=c.Field("CIF")} " – netadictos Aug 15 '18 at 17:51
3
var Results= query.GroupBy(f => new { /* add members here */  });
sepehr
  • 651
  • 11
  • 26
Arindam
  • 176
  • 1
  • 5
3

A thing to note is that you need to send in an object for Lambda expressions and can't use an instance for a class.

Example:

public class Key
{
    public string Prop1 { get; set; }

    public string Prop2 { get; set; }
}

This will compile but will generate one key per cycle.

var groupedCycles = cycles.GroupBy(x => new Key
{ 
  Prop1 = x.Column1, 
  Prop2 = x.Column2 
})

If you wan't to name the key properties and then retreive them you can do it like this instead. This will GroupBy correctly and give you the key properties.

var groupedCycles = cycles.GroupBy(x => new 
{ 
  Prop1 = x.Column1, 
  Prop2= x.Column2 
})

foreach (var groupedCycle in groupedCycles)
{
    var key = new Key();
    key.Prop1 = groupedCycle.Key.Prop1;
    key.Prop2 = groupedCycle.Key.Prop2;
}
Ogglas
  • 38,157
  • 20
  • 203
  • 266
2
.GroupBy(x => x.Column1 + " " + x.Column2)
Kai Hartmann
  • 2,876
  • 25
  • 38
  • Combined with `Linq.Enumerable.Aggregate()` this even allows for grouping by a dynamic number of properties: `propertyValues.Aggregate((current, next) => current + " " + next)`. – Kai Hartmann Aug 01 '17 at 07:51
  • 3
    This is a better answer than anyone is giving credit for. It may be problematic if there could be instances of combinations where column1 added to column2 would equal the same thing for situations where column1 differs ("ab" "cde" would match "abc" "de"). That said, this is a great solution if you can't use a dynamic type because you are pre-constructing lambdas after the group by in separate expressions. – Brandon Barkley Apr 19 '18 at 19:14
  • 3
    "ab" "cde" should actually not match "abc" "de", hence the blank in between. – Kai Hartmann May 08 '18 at 06:29
  • 1
    what about "abc de" "" and "abc" "de "? – AlbertK Mar 15 '19 at 19:24
  • @AlbertK I'm afraid that wouldn't work. – Kai Hartmann Nov 30 '20 at 15:10
2

group x by new { x.Col, x.Col}

John
  • 464
  • 3
  • 15
2

.GroupBy(x => (x.MaterialID, x.ProductID))

Let's Enkindle
  • 138
  • 2
  • 17
1

For VB and anonymous/lambda:

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Dani
  • 1,345
  • 13
  • 23