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
Sreedhar
quelle

Antworten:

1212

Verwenden Sie einen anonymen Typ.

Z.B

group x by new { x.Column1, x.Column2 }
Leppie
quelle
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 })
Mo0gles
quelle
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();
Sreedhar
quelle
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.

Mailand
quelle
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
30

Seit C # 7 können Sie auch Wertetupel verwenden:

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

oder

.GroupBy(x => (x.Column1, x.Column2))
Nathan Tregillus
quelle
2
Nun, ich denke du vermisst ein Extra) am Ende. Sie schließen nicht die ()
StuiterSlurf
Ich habe es hinzugefügt.
Nathan Tregillus
2
.GroupBy (x => new {x.Column1, x.Column2})
Zahidul Islam Jamy
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) });
AlbertK
quelle
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),
}
Jay Bienvenu
quelle
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);
Chris Smith
quelle
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 */  });
Arindam
quelle
7
Fügt den vorherigen Antworten nichts hinzu.
Mike Fuchs
4
.GroupBy(x => x.Column1 + " " + x.Column2)
Kai Hartmann
quelle
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
3

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

Lass uns entzünden
quelle
3
Fügen Sie eine Erklärung hinzu, wie dieser Code das Problem löst.
Rosário Pereira Fernandes
2

gruppiere x nach neu {x.Col, x.Col}

John
quelle
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;
}
Ogglas
quelle
0

Für VB und anonym / Lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Dani
quelle