Agrupar por múltiples columnas


968

¿Cómo puedo hacer GroupBy Multiple Columns en LINQ?

Algo similar a esto en SQL:

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

¿Cómo puedo convertir esto a 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

Respuestas:


1213

Use un tipo anónimo.

P.ej

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

29
Si eres nuevo en la agrupación con tipos anónimos, el uso de la palabra clave 'nuevo' en este ejemplo hace la magia.
Chris

8
en caso de mvc con nHibernate obteniendo error por problemas de dll. Problema resuelto por GroupBy (x => new {x.Column1, x.Column2}, (key, group) => new {Key1 = key.Column1, Key2 = key.Column2, Result = group.ToList ()});
Milán

Pensé que en este caso los nuevos objetos se compararían por referencia, por lo que no coincidiría, no se agruparía.
HoGo

44
Los objetos mecanografiados anónimos de @HoGo implementan sus propios métodos Equals y GetHashCode que se utilizan al agrupar los objetos.
Byron Carasco

Un poco difícil de visualizar la estructura de datos de salida cuando eres nuevo en Linq. ¿Esto crea una agrupación donde se usa el tipo anónimo como clave?
Jacques

760

Muestra de procedimiento

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

¿De qué tipo se devuelve el objeto?
mggSoft 01 de

55
@MGG_Soft que sería un tipo anónimo
Alex

Este código no me funciona: "Declarador de tipo anónimo no válido".
thalesfc

19
@ Tom esto debería funcionar como está. Cuando omite nombrar los campos de un tipo anónimo, C # supone que desea utilizar el nombre de la propiedad / campo finalmente accedido desde la proyección. (Entonces su ejemplo es equivalente a Mo0gles ')
Chris Pfohl

1
Encontré mi respuesta. Necesito definir una nueva entidad (MyViewEntity) que contenga las propiedades Column1 y Column2 y el tipo de retorno es: IEnumerable <IGrouping <MyViewEntity, MyEntity >> y el fragmento de código de agrupación es: MyEntityList.GroupBy (myEntity => new MyViewEntity {Column1 = myEntity. Column1, Column2 = myEntity.Column2});
Amir Chatrbahr

469

Ok tengo esto como:

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 - Gracias por el ejemplo completo. Los fragmentos de la otra respuesta son demasiado cortos y sin contexto. También muestra una función agregada (Suma en este caso). Muy útil. Encuentro el uso de una función agregada (es decir, MAX, MIN, SUM, etc.) junto con la agrupación como un escenario común.
barrypicker

Aquí: stackoverflow.com/questions/14189537/… , se muestra para una tabla de datos cuando la agrupación se basa en una sola columna, cuyo nombre se conoce, pero ¿cómo se puede hacer si las columnas se basan en la agrupación? tiene que ser generado dinámicamente?
bg

Esto es realmente útil para comprender el concepto de agrupación y aplicación de agregación sobre él.
rajibdotnet

1
Gran ejemplo ... justo lo que estaba buscando. Incluso necesitaba el agregado, así que esta fue la respuesta perfecta, aunque estaba buscando lambda, obtuve suficiente para resolver mis necesidades.
Kevbo

153

Para Agrupar por varias columnas, intente esto en su lugar ...

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

De la misma manera, puede agregar Column3, Column4, etc.


44
¡Eso fue muy útil y debería recibir muchos más votos a favor! Resultcontiene todos los conjuntos de datos vinculados a todas las columnas. ¡Muchas gracias!
j00hi

1
nota: tuve que usar .AsEnumerable () en lugar de ToList ()
GMan

1
Impresionante, gracias por esto. Aquí está mi ejemplo. Tenga en cuenta que GetFees devuelve un IQueryable <Fee> 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

¿Es posible obtener otras columnas de esta consulta, que no se agruparon? Si hay una matriz de objetos, me gustaría agrupar este objeto en dos columnas, pero obtener todas las propiedades del objeto, no solo esas dos columnas.
FrenkyB


19

C # 7.1 o superior utilizando Tuplesy Inferred tuple element names(actualmente sólo funciona con linq to objectsy no se admite cuando los árboles de expresión se requieren por ejemplo someIQueryable.GroupBy(...). Tema 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 superior usando 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

También puede usar una Tupla <> para una agrupación fuertemente tipada.

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

44
Nota: Crear una nueva Tupla no es compatible con Linq To Entities
foolmoron

8

Aunque esta pregunta es acerca de las propiedades de agrupar por clase, si desea agrupar por múltiples columnas contra un objeto ADO (como un DataTable), debe asignar sus elementos "nuevos" a las 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);

1
No pude hacer la consulta Linq "grupo c por nuevo {c.Field <String> (" Título "), c.Field <String> (" CIF ")}", ¡y me ahorró mucho tiempo! la consulta final fue: "grupo c por nuevo {titulo = c.Field <String> (" Título "), cif = c.Field <String> (" CIF ")}"
netadictos

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

77
No agrega nada a las respuestas anteriores.
Mike Fuchs

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

En combinación con Linq.Enumerable.Aggregate()esto incluso permite la agrupación de un número dinámico de propiedades: propertyValues.Aggregate((current, next) => current + " " + next).
Kai Hartmann

3
Esta es una mejor respuesta de la que nadie está dando crédito. Puede ser problemático si podría haber instancias de combinaciones en las que la columna 1 agregada a la columna 2 sería igual para situaciones en las que la columna 1 difiere ("ab" "cde" coincidiría con "abc" "de"). Dicho esto, esta es una gran solución si no puede usar un tipo dinámico porque está preconstruyendo lambdas después del grupo en expresiones separadas.
Brandon Barkley

3
"ab" "cde" en realidad no debería coincidir con "abc" "de", de ahí el espacio en blanco en el medio.
Kai Hartmann,

1
¿Qué pasa con "abc de" "" y "abc" "de"?
AlbertK

@AlbertK eso no funcionará, supongo.
Kai Hartmann



1

Una cosa a tener en cuenta es que debe enviar un objeto para las expresiones Lambda y no puede usar una instancia para una clase.

Ejemplo:

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

    public string Prop2 { get; set; }
}

Esto compilará pero generará una clave por ciclo .

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

Si no quiere nombrar las propiedades clave y luego recuperarlas, puede hacerlo así. Esto lo hará GroupBycorrectamente y le dará las propiedades clave.

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

Para VB y anónimo / lambda :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.