Nach mehreren Spalten gruppieren


967

Wie kann ich GroupBy Multiple Columns in LINQ ausführen?

Ähnliches in SQL:

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

Wie kann ich dies in LINQ konvertieren:

QuantityBreakdown
(
    MaterialID int,
    ProductID int,
    Quantity float
)

INSERT INTO @QuantityBreakdown (MaterialID, ProductID, Quantity)
SELECT MaterialID, ProductID, SUM(Quantity)
FROM @Transactions
GROUP BY MaterialID, ProductID

Antworten:


1212

Verwenden Sie einen anonymen Typ.

Z.B

group x by new { x.Column1, x.Column2 }

29
Wenn Sie mit dem Gruppieren mit anonymen Typen noch nicht vertraut sind, ist die Verwendung des Schlüsselworts "new" in diesem Beispiel von Vorteil.
Chris

8
im Falle von MVC mit nHibernate wird ein Fehler für DLL-Probleme angezeigt. Problem behoben durch GroupBy (x => new {x.Column1, x.Column2}, (key, group) => new {Key1 = key.Column1, Key2 = key.Column2, Result = group.ToList ()});
Mailand

Ich dachte in diesem Fall würden die neuen Objekte durch Referenz verglichen, also keine Übereinstimmung - kein Groupping.
HoGo

4
Anonyme typisierte @ HoGo-Objekte implementieren ihre eigenen Equals- und GetHashCode- Methoden, die beim Gruppieren der Objekte verwendet werden.
Byron Carasco

Es ist etwas schwierig, die Struktur der Ausgabedaten zu visualisieren, wenn Sie neu bei Linq sind. Erstellt dies eine Gruppierung, in der der anonyme Typ als Schlüssel verwendet wird?
Jacques

760

Verfahrensprobe

.GroupBy(x => new { x.Column1, x.Column2 })

Welchen Typ hat das Objekt zurückgegeben?
mggSoft

5
@MGG_Soft das wäre ein anonymer Typ
Alex

Dieser Code funktioniert bei mir nicht: "Ungültiger anonymer Typdeklarator."
Thalesfc

19
@ Tom das sollte so funktionieren wie es ist. Wenn Sie das Benennen der Felder eines anonymen Typs überspringen, geht C # davon aus, dass Sie den Namen der Eigenschaft / des Felds verwenden möchten, auf die bzw. das schließlich aus der Projektion zugegriffen wird. (Ihr Beispiel entspricht also Mo0gles ')
Chris Pfohl

1
fand meine Antwort. Ich muss eine neue Entität (MyViewEntity) definieren, die die Eigenschaften Column1 und Column2 enthält. Der Rückgabetyp lautet: IEnumerable <IGrouping <MyViewEntity, MyEntity >> und Grouping Code Snip lautet: MyEntityList.GroupBy (myEntity => new MyViewEntity {Column1 = myEntity. Column1, Column2 = myEntity.Column2});
Amir Chatrbahr

468

Ok habe das als:

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();

75
+1 - Danke für das umfassende Beispiel. Die Ausschnitte der anderen Antwort sind zu kurz und ohne Kontext. Außerdem zeigen Sie eine Aggregatfunktion (in diesem Fall Summe). Sehr hilfreich. Ich finde die Verwendung einer Aggregatfunktion (dh MAX, MIN, SUM usw.) neben der Gruppierung ein häufiges Szenario.
Barrypicker

Hier: stackoverflow.com/questions/14189537/… wird es für eine Datentabelle angezeigt , wenn die Gruppierung auf einer einzelnen Spalte basiert, deren Name bekannt ist. Wie kann dies durchgeführt werden, wenn Spalten basierend auf der Gruppierung durchgeführt werden sollen? muss dynamisch generiert werden?
BG

Dies ist sehr hilfreich, um das Konzept der Gruppierung und Anwendung der Aggregation zu verstehen.
Rajibdotnet

1
Tolles Beispiel ... genau das, wonach ich gesucht habe. Ich brauchte sogar das Aggregat, also war dies die perfekte Antwort, obwohl ich nach Lambda suchte, bekam ich genug davon, um meine Bedürfnisse zu lösen.
Kevbo

153

Versuchen Sie dies stattdessen für Gruppieren nach mehreren Spalten ...

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

Genauso können Sie Column3, Column4 usw. hinzufügen.


4
Das war sehr hilfreich und sollte viel mehr positive Stimmen bekommen! Resultenthält alle Datensätze, die mit allen Spalten verknüpft sind. Vielen Dank!
J00hi

1
Hinweis: Ich musste .AsEnumerable () anstelle von ToList () verwenden
GMan

1
Super, danke dafür. Hier ist mein Beispiel. Beachten Sie, dass GetFees eine IQueryable <Fee> RegistryAccountDA.GetFees (registryAccountId, fromDate, toDate) zurückgibt. GroupBy (x => new {x.AccountId, x.FeeName}, (Schlüssel, Gruppe) => new {AccountId = key.AccountId , FeeName = key.FeeName, AppliedFee = group.Sum (x => x.AppliedFee) ?? 0M}). ToList ();
Craig B

Ist es möglich, andere Spalten aus dieser Abfrage abzurufen, die nicht gruppiert wurden? Wenn es ein Array von Objekten gibt, möchte ich dieses Objekt in zwei Spalten gruppieren, aber alle Eigenschaften des Objekts abrufen, nicht nur diese beiden Spalten.
FrenkyB


19

C # 7.1 oder höher mit Tuplesund Inferred tuple element names(derzeit funktioniert es nur mit linq to objectsund wird nicht unterstützt, wenn Ausdrucksbäume erforderlich sind, z someIQueryable.GroupBy(...). B. Github-Problem ):

// 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 oder höher mit 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) });

18

Sie können auch ein Tupel <> für eine stark typisierte Gruppierung verwenden.

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),
}

4
Hinweis: Das Erstellen eines neuen Tupels wird in Linq To Entities
Foolmoron

8

Diese Frage bezieht sich zwar auf Eigenschaften nach Gruppierung nach Klasse. Wenn Sie jedoch mehrere Spalten für ein ADO-Objekt (wie eine DataTable) gruppieren möchten, müssen Sie Ihre "neuen" Elemente Variablen zuweisen:

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);

1
Ich konnte die Linq-Abfrage "Gruppe c nach neuem {c.Field <String> (" Titel "), c.Field <String> (" CIF ")}" nicht ausführen, und Sie haben mir viel Zeit gespart !! Die letzte Abfrage lautete: "Gruppe c nach neuem {titulo = c.Field <String> (" Titel "), cif = c.Field <String> (" CIF ")}"
netadictos

4
var Results= query.GroupBy(f => new { /* add members here */  });

7
Fügt den vorherigen Antworten nichts hinzu.
Mike Fuchs

4
.GroupBy(x => x.Column1 + " " + x.Column2)

In Kombination Linq.Enumerable.Aggregate()damit kann sogar nach einer dynamischen Anzahl von Eigenschaften gruppiert werden : propertyValues.Aggregate((current, next) => current + " " + next).
Kai Hartmann

3
Dies ist eine bessere Antwort, als irgendjemand anerkennt. Es kann problematisch sein, wenn es Fälle von Kombinationen geben kann, in denen Spalte1, die zu Spalte2 hinzugefügt werden, in Situationen, in denen Spalte1 unterschiedlich ist, gleich ist ("ab" "cde" würde mit "abc" "de" übereinstimmen). Dies ist jedoch eine großartige Lösung, wenn Sie keinen dynamischen Typ verwenden können, da Sie Lambdas nach der Gruppe in separaten Ausdrücken vorkonstruieren.
Brandon Barkley

3
"ab" "cde" sollte eigentlich nicht mit "abc" "de" übereinstimmen, daher das Leerzeichen dazwischen.
Kai Hartmann

1
was ist mit "abc de" "" und "abc" "de"?
AlbertK

@ AlbertK das wird nicht funktionieren, denke ich.
Kai Hartmann



1

Beachten Sie, dass Sie ein Objekt für Lambda-Ausdrücke senden müssen und keine Instanz für eine Klasse verwenden können.

Beispiel:

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

    public string Prop2 { get; set; }
}

Dies wird kompiliert, generiert jedoch einen Schlüssel pro Zyklus .

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

Wenn Sie die Schlüsseleigenschaften nicht benennen und dann abrufen möchten, können Sie dies stattdessen so tun. Dies wird GroupBykorrekt sein und Ihnen die Schlüsseleigenschaften geben.

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;
}

0

Für VB und anonym / Lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.