Grupuj według wielu kolumn przy użyciu lambda

122

Jak mogę grupować według wielu kolumn przy użyciu lambda?

Widziałem przykłady, jak to zrobić za pomocą linq do encji, ale szukam formy lambda.

Naor
źródło

Odpowiedzi:

253
var query = source.GroupBy(x => new { x.Column1, x.Column2 });
Aducci
źródło
Czy to faktycznie zadziała? Myślę, że test równości dla każdego obiektu, według którego grupujesz się, nie powiedzie się, ponieważ są to obiekty, a nie struktury.
Jacob
@Aducci: Dzięki. Czy możesz na przykład, jak mogę uzyskać IEnumerable z elementów grupy?
Naor
6
@Jacob - typy anonimowe to niezmienne klasy z odpowiednio nadpisanymi GetHashCode& Equalsmetodami. Zostały zaprojektowane dla dokładnie tego rodzaju przypadków użycia.
Enigmativity
5
@Naor - GroupByzwraca, IEnumerable<IGrouping<TKey, TSource>>który jest zasadniczo an IEnumerable<IEnumerable<TSource>>z Keywłaściwością wewnętrznego wyliczenia. Czy to pomoże Ci uzyskać „IEnumerable” elementów grupy?
Enigmativity
Jeśli moja zmienna „źródłowa” jest zbiorem słownikowym, to nie działa. Propozycje?
Joao Paulo,
6

jeśli twój stół jest taki

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
źródło
6
Bardzo ważne jest, aby pamiętać, że AsEnumerable wprowadzi całą tabelę do pamięci przed jej zgrupowaniem. To zdecydowanie ma znaczenie w przypadku niektórych stołów. Zobacz tę odpowiedź, aby uzyskać więcej informacji: stackoverflow.com/questions/17968469/…
Brandon Barkley
4

W nawiązaniu do powyższej odpowiedzi aduchis - jeśli następnie potrzebujesz filtrować w oparciu o te grupy według kluczy, możesz zdefiniować klasę, aby opakować wiele kluczy.

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

Gdzie CustomerGroupingKey bierze klucze grupowe:

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

        public string Country { get; }

        public string Gender { get; }
    }
David McEleney
źródło
1
Zapewne zaoszczędzi komuś trochę czasu: lepiej używać domyślnych konstrukcji z inicjatorami obiektów. Podejście w powyższym przykładowym kodzie nie będzie traktowane przez ORMy, takie jak 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
źródło
1

Wymyśliłem kombinację definiowania klasy, takiej jak odpowiedź Davida, ale nie wymagającej klasy Where, aby z nią iść. Wygląda to mniej więcej tak:

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

Gdzie resultRecordsjest moja początkowa lista, którą grupuję, a to List<ResultCsvImport>. Zwróć uwagę, że chodzi o to, że grupuję według 3 kolumn, IdObj1 i IdObj2 i IdObj3

Jeff Moretti
źródło