Raggruppa per più colonne


967

Come posso fare GroupBy su più colonne in LINQ

Qualcosa di simile a questo in SQL:

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

Come posso convertirlo in 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

Risposte:


1213

Usa un tipo anonimo.

Per esempio

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

29
Se sei nuovo nel raggruppamento con tipi anonimi, l'uso della parola chiave "new" in questo esempio fa la magia.
Chris,

8
in caso di mvc con nHibernate ottenere errori per problemi di dll. Problema risolto da GroupBy (x => new {x.Column1, x.Column2}, (key, group) => new {Key1 = key.Column1, Key2 = key.Column2, Result = group.ToList ()});
Milano,

In questo caso, ho pensato che i nuovi oggetti sarebbero stati confrontati per riferimento, quindi nessuna corrispondenza - nessun groupping.
HoGo

4
Gli oggetti digitati anonimi @HoGo implementano i propri metodi Equals e GetHashCode utilizzati durante il raggruppamento degli oggetti.
Byron Carasco,

Un po 'difficile visualizzare la struttura dei dati di output quando sei nuovo su Linq. Questo crea un raggruppamento in cui il tipo anonimo viene utilizzato come chiave?
Jacques,

760

Campione procedurale

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

Che tipo viene restituito l'oggetto?
mggSoft,

5
@MGG_Soft sarebbe un tipo anonimo
Alex

Questo codice non funziona per me: "Dichiaratore di tipo anonimo non valido."
Thalesfc,

19
@Tom dovrebbe funzionare così com'è. Quando si salta la denominazione dei campi di un tipo anonimo C #, si presuppone che si desidera utilizzare il nome della proprietà / campo finalmente accessibile dalla proiezione. (Quindi il tuo esempio è equivalente a Mo0gles ')
Chris Pfohl,

1
ho trovato la mia risposta. Devo definire una nuova entità (MyViewEntity) contenente le proprietà Column1 e Column2 e il tipo restituito è: IEnumerable <IGrouping <MyViewEntity, MyEntity >> e Snip del codice di raggruppamento è: MyEntityList.GroupBy (myEntity => new MyViewEntity {Column1 = myEntity. Column1, Column2 = myEntity.Column2});
Amir Chatrbahr,

469

Ok ho capito come:

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 - Grazie per l'esempio completo. Gli snippet dell'altra risposta sono troppo brevi e senza contesto. Inoltre si mostra una funzione aggregata (Somma in questo caso). Molto utile. Trovo che l'uso di una funzione aggregata (ovvero MAX, MIN, SUM, ecc.) Affiancata al raggruppamento sia uno scenario comune.
barrypicker,

Qui: stackoverflow.com/questions/14189537/… , viene mostrato per una tabella di dati quando il raggruppamento si basa su una singola colonna, il cui nome è noto, ma come si può fare se si basano le colonne in base alle quali deve essere eseguito il raggruppamento deve essere generato dinamicamente?
bg

Questo è davvero utile per comprendere il concetto di raggruppamento e applicare l'aggregazione su di esso.
rajibdotnet,

1
Ottimo esempio ... proprio quello che stavo cercando. Avevo persino bisogno dell'aggregato, quindi questa era la risposta perfetta anche se stavo cercando lambda, ne ho avuto abbastanza per risolvere i miei bisogni.
Kevbo,

153

Per Raggruppa per più colonne, prova invece ...

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

Allo stesso modo è possibile aggiungere Column3, Column4 ecc.


4
È stato molto utile e dovrebbe ricevere molti più voti! Resultcontiene tutti i set di dati collegati a tutte le colonne. Molte grazie!
j00hi,

1
nota: ho dovuto usare .AsEnumerable () invece di ToList ()
GMan

1
Fantastico, grazie per questo. Ecco il mio esempio Si noti che GetFees restituisce un IQueryable <Fee> RegistryAccountDA.GetFees (registerAccountId, 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,

È possibile ottenere altre colonne da questa query, che non sono state raggruppate? Se esiste una matrice di oggetti, vorrei raggruppare l'oggetto in base a due colonne, ma ottenere tutte le proprietà dall'oggetto, non solo quelle due colonne.
FrenkyB,


19

C # 7.1 o versioni successive usando Tuplese Inferred tuple element names(attualmente funziona solo con linq to objectse non è supportato quando sono richiesti alberi delle espressioni, ad es someIQueryable.GroupBy(...). Problema con Github ):

// 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 o superiore utilizzando 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

Puoi anche usare una Tupla <> per un raggruppamento fortemente tipizzato.

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
Nota: la creazione di una nuova tupla non è supportata in Linq To Entities
foolmoron,

8

Sebbene questa domanda ponga le proprietà di raggruppamento per classe, se si desidera raggruppare per più colonne su un oggetto ADO (come una DataTable), è necessario assegnare i "nuovi" elementi alle variabili:

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
Non ho potuto eseguire la query Linq "gruppo c con il nuovo {c.Field <String> (" Title "), c.Field <String> (" CIF ")}" e mi hai fatto risparmiare un sacco di tempo !! la query finale era: "gruppo c per nuovo {titulo = c.Field <String> (" Titolo "), cif = c.Field <String> (" CIF ")}"
netadictos,

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

7
Non aggiunge nulla alle risposte precedenti.
Mike Fuchs,

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

Combinato con Linq.Enumerable.Aggregate()questo anche permette di raggruppare da una serie dinamica di proprietà: propertyValues.Aggregate((current, next) => current + " " + next).
Kai Hartmann,

3
Questa è una risposta migliore di quella che chiunque sta dando credito. Può essere problematico se potrebbero esserci casi di combinazioni in cui colonna1 aggiunta a colonna2 equivarrebbe alla stessa cosa per situazioni in cui colonna1 differisce ("ab" "cde" corrisponderebbe a "abc" "de"). Detto questo, questa è un'ottima soluzione se non puoi usare un tipo dinamico perché stai pre-costruendo lambda dopo il gruppo in espressioni separate.
Brandon Barkley,

3
"ab" "cde" in realtà non dovrebbe corrispondere a "abc" "de", quindi il vuoto tra.
Kai Hartmann,

1
che dire di "abc de" "" e "abc" "de"?
AlbertK,

@AlbertK che non funzionerà, immagino.
Kai Hartmann,



1

Una cosa da notare è che è necessario inviare un oggetto per le espressioni Lambda e non è possibile utilizzare un'istanza per una classe.

Esempio:

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

    public string Prop2 { get; set; }
}

Questo verrà compilato ma genererà una chiave per ciclo .

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

Se non vuoi nominare le proprietà chiave e poi recuperarle, puoi farlo in questo modo. Questo funzionerà GroupBycorrettamente e ti darà le proprietà chiave.

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

Per VB e anonimo / lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.