Agrupar por con múltiples columnas usando lambda

122

¿Cómo puedo agrupar con múltiples columnas usando lambda?

Vi ejemplos de cómo hacerlo usando linq para entidades, pero estoy buscando la forma lambda.

Naor
fuente

Respuestas:

252
var query = source.GroupBy(x => new { x.Column1, x.Column2 });
Aducci
fuente
¿Funcionaría esto realmente? Creo que la prueba de igualdad para cada objeto por el que está agrupando fallaría ya que son objetos y no estructuras.
Jacob
@Aducci: Gracias. ¿Puede un ejemplo de cómo puedo obtener IEnumerable de los elementos del grupo?
Naor
6
@Jacob: los tipos anónimos son clases inmutables con métodos GetHashCodey anulados correctamente Equals. Fueron diseñados exactamente para este tipo de caso de uso.
Enigmativity
5
@Naor: GroupBydevuelve un IEnumerable<IGrouping<TKey, TSource>>que es esencialmente un IEnumerable<IEnumerable<TSource>>con una Keypropiedad en el enumerable interno. ¿Eso le ayuda a obtener el "IEnumerable" de los elementos del grupo?
Enigmativity
Si mi variable 'fuente' es una colección de diccionario, esto no funciona. Sugerencias
Joao Paulo
6

si tu mesa es asi

rowId     col1    col2    col3    col4
 1          a       e       12       2
 2          b       f       42       5
 3          a       e       32       2
 4          b       f       44       5


var grouped = myTable.AsEnumerable().GroupBy(r=> new {pp1 =  r.Field<int>("col1"), pp2 = r.Field<int>("col2")});
Jhankar Mahbub
fuente
6
Es muy importante tener en cuenta que AsEnumerable traerá toda la tabla a la memoria antes de agruparla. Eso definitivamente importa en algunas mesas. Consulte esta respuesta para obtener más información: stackoverflow.com/questions/17968469/…
Brandon Barkley
4

Además de la respuesta de aduchis anterior, si luego necesita filtrar en función de esos grupos por claves, puede definir una clase para envolver las muchas claves.

return customers.GroupBy(a => new CustomerGroupingKey(a.Country, a.Gender))
                .Where(a => a.Key.Country == "Ireland" && a.Key.Gender == "M")
                .SelectMany(a => a)
                .ToList();

Donde CustomerGroupingKey toma las claves de grupo:

    private class CustomerGroupingKey
    {
        public CustomerGroupingKey(string country, string gender)
        {
            Country = country;
            Gender = gender;
        }

        public string Country { get; }

        public string Gender { get; }
    }
David McEleney
fuente
1
Probablemente le ahorrará a alguien algo de tiempo: es mejor usar construcciones predeterminadas con inicializadores de objetos. El enfoque en el código de muestra anterior no será tratado bien por ORM como EF Core.
Konstantin
2
     class Element
        {
            public string Company;        
            public string TypeOfInvestment;
            public decimal Worth;
        }

   class Program
    {
        static void Main(string[] args)
        {
         List<Element> elements = new List<Element>()
            {
                new Element { Company = "JPMORGAN CHASE",TypeOfInvestment = "Stocks", Worth = 96983 },
                new Element { Company = "AMER TOWER CORP",TypeOfInvestment = "Securities", Worth = 17141 },
                new Element { Company = "ORACLE CORP",TypeOfInvestment = "Assets", Worth = 59372 },
                new Element { Company = "PEPSICO INC",TypeOfInvestment = "Assets", Worth = 26516 },
                new Element { Company = "PROCTER & GAMBL",TypeOfInvestment = "Stocks", Worth = 387050 },
                new Element { Company = "QUASLCOMM INC",TypeOfInvestment = "Bonds", Worth = 196811 },
                new Element { Company = "UTD TECHS CORP",TypeOfInvestment = "Bonds", Worth = 257429 },
                new Element { Company = "WELLS FARGO-NEW",TypeOfInvestment = "Bank Account", Worth = 106600 },
                new Element { Company = "FEDEX CORP",TypeOfInvestment = "Stocks", Worth = 103955 },
                new Element { Company = "CVS CAREMARK CP",TypeOfInvestment = "Securities", Worth = 171048 },
            };

            //Group by on multiple column in LINQ (Query Method)
            var query = from e in elements
                        group e by new{e.TypeOfInvestment,e.Company} into eg
                        select new {eg.Key.TypeOfInvestment, eg.Key.Company, Points = eg.Sum(rl => rl.Worth)};



            foreach (var item in query)
            {
                Console.WriteLine(item.TypeOfInvestment.PadRight(20) + " " + item.Points.ToString());
            }


            //Group by on multiple column in LINQ (Lambda Method)
            var CompanyDetails =elements.GroupBy(s => new { s.Company, s.TypeOfInvestment})
                               .Select(g =>
                                            new
                                            {
                                                company = g.Key.Company,
                                                TypeOfInvestment = g.Key.TypeOfInvestment,            
                                                Balance = g.Sum(x => Math.Round(Convert.ToDecimal(x.Worth), 2)),
                                            }
                                      );
            foreach (var item in CompanyDetails)
            {
                Console.WriteLine(item.TypeOfInvestment.PadRight(20) + " " + item.Balance.ToString());
            }
            Console.ReadLine();

        }
    }
Sandeep Tripathi
fuente
1

Se me ocurrió una combinación de definir una clase como la respuesta de David, pero sin requerir una clase Where para ir con ella. Se parece a algo como:

var resultsGroupings = resultsRecords.GroupBy(r => new { r.IdObj1, r.IdObj2, r.IdObj3})
                                    .Select(r => new ResultGrouping {
                                        IdObj1= r.Key.IdObj1,
                                        IdObj2= r.Key.IdObj2,
                                        IdObj3= r.Key.IdObj3,
                                        Results = r.ToArray(),
                                        Count = r.Count()
                                    });



private class ResultGrouping
        {
            public short IdObj1{ get; set; }
            public short IdObj2{ get; set; }
            public int IdObj3{ get; set; }

            public ResultCsvImport[] Results { get; set; }
            public int Count { get; set; }
        }

¿Dónde resultRecordsestá mi lista inicial que estoy agrupando y es un List<ResultCsvImport>. Tenga en cuenta que la idea aquí es que estoy agrupando por 3 columnas, IdObj1 e IdObj2 e IdObj3

Jeff Moretti
fuente