Згрупуйте за кількома стовпцями


967

Як я можу зробити GroupBy кілька стовпців у LINQ

Щось подібне до цього в SQL:

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

Як конвертувати це в 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

Відповіді:


1212

Використовуйте анонімний тип.

Напр

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

29
Якщо ви новачок у групуванні з анонімними типами, використання ключового слова "new" у цьому прикладі робить магію.
Кріс

8
у випадку, коли mvc з nHibernate отримує помилку щодо проблем DLL. Проблема вирішена GroupBy (x => new {x.Column1, x.Column2}, (key, group) => new {Key1 = key.Column1, Key2 = key.Column2, Result = group.ToList ()});
Мілан

Я думав, що в цьому випадку нові об'єкти будуть порівнюватися за посиланням, тому ніякої відповідності - ніякої групування.
HoGo

4
Анонімні типізовані об’єкти @HoGo реалізують власні методи Equals і GetHashCode, які використовуються при групуванні об'єктів.
Байрон Караско

Трохи важко візуалізувати структуру вихідних даних, коли ви новачок у Linq. Чи створює це групування, де анонімний тип використовується як ключ?
Жак

760

Процедурна проба

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

Якого типу повертається об’єкт?
mggSoft

5
@MGG_Soft це буде анонімний тип
Алекс

Цей код для мене не працює: "Недійсний декларатор анонімного типу."
thalesfc

19
@Том це має працювати так, як є. Якщо ви пропускаєте іменування полів анонімного типу, C # передбачає, що ви хочете використовувати ім'я остаточно доступного властивості / поля з проекції. (Отже, ваш приклад еквівалентний Mo0gles ')
Кріс Пфол,

1
знайшов свою відповідь. Мені потрібно визначити нову сутність (MyViewEntity), що містить властивості Column1 та Column2, а тип повернення: IEnumerable <IGrouping <MyViewEntity, MyEntity >> та фрагмент коду групування: MyEntityList.GroupBy (myEntity => новий MyViewEntity {Column1 = myEntity Стовпчик1, стовпчик2 = myEntity.Column2});
Amir Chatrbahr

467

Добре це зрозуміло:

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 - Дякую за вичерпний приклад. Інші фрагменти відповіді занадто короткі та без контексту. Також ви показуєте сукупну функцію (Сума в цьому випадку). Дуже корисний. Я вважаю, що використання сукупної функції (тобто MAX, MIN, SUM тощо) поряд із групуванням є загальним сценарієм.
barrypicker

Тут: stackoverflow.com/questions/14189537/… , він відображається для таблиці даних, коли групування базується на одному стовпчику, ім'я якого відомо, але як це можна зробити, якщо стовпці, на основі яких проводиться групування. має генеруватися динамічно?
bg

Це дуже корисно для розуміння концепції групування та застосування агрегації над нею.
rajibdotnet

1
Чудовий приклад ... просто те, що я шукав. Мені навіть потрібен був агрегат, тому це була ідеальна відповідь, хоча я шукав лямбда, я отримав достатньо для цього, щоб вирішити свої потреби.
Кевбо

153

Для групи за кількома стовпцями спробуйте це замість ...

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

Таким же чином ви можете додати Column3, Column4 і т.д.


4
Це було дуже корисно і повинно отримати набагато більше результатів! Resultмістить усі набори даних, пов'язані з усіма стовпцями. Дуже дякую!
j00hi

1
Примітка: мені довелося використовувати .AsEumerable () замість ToList ()
GMan

1
Дивовижне, дякую за це. Ось мій приклад. Зауважте, що GetFees повертає IQueryable <Fee> RegistryAccountDA.GetFees (registerAccountId, fromDate, toDate) .GroupBy (x => new {x.AccountId, x.FeeName}, (ключ, група) => новий {AccountId = key.AccountId , FeeName = key.FeeName, AppliedFee = group.Sum (x => x.AppliedFee) ?? 0M}). ToList ();
Крейг Б

Чи можливо отримати з цього запиту інші стовпці, які не були згруповані? Якщо є масив об’єктів, я хотів би об'єднати цей об’єкт, згрупований у два стовпці, але отримати всі властивості від об'єкта, а не лише ці два стовпці.
FrenkyB

30

Оскільки на C # 7 ви також можете використовувати цінні кортежі:

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

або

.GroupBy(x => (x.Column1, x.Column2))

2
Добре, я думаю, вам не вистачає додаткового) наприкінці. Ви не закриваєте ()
StuiterSlurf

Я це додав.
Натан Трегілл

2
.GroupBy (x => new {x.Column1, x.Column2})
Західул Іслам Джеймі

19

Використовуючи C # 7.1 або новішу версію Tuplesта Inferred tuple element names(на даний момент вона працює лише з linq to objectsі не підтримується, коли потрібні дерева виразів, наприклад someIQueryable.GroupBy(...). Випуск 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 або вище, використовуючи 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

Ви також можете використовувати кортеж <> для сильно набраного групування.

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
Примітка: Створення нового кортежу не підтримується в Linq To
Entities

8

Хоча це питання задає питання про групування за властивостями класу, якщо ви хочете згрупувати декілька стовпців проти об’єкта ADO (наприклад, DataTable), вам слід призначити "нові" елементи змінним:

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
Я не міг виконати запит Linq "group c новими {c.Field <String> (" Назва "), c.Field <String> (" CIF ")}", і ти врятував мені багато часу !! остаточним запитом було: "група c по новому {titulo = c.Field <String> (" Заголовок "), cif = c.Field <String> (" CIF ")}"
netadictos


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

У поєднанні з Linq.Enumerable.Aggregate()цим навіть дозволяє групувати динамічним рядом властивостей: propertyValues.Aggregate((current, next) => current + " " + next).
Кай Хартманн

3
Це краща відповідь, ніж хтось дає кредит. Це може бути проблематично, якщо можуть бути випадки комбінацій, коли колонка1, додана до колонки2, буде однаковою для ситуацій, коли стовпець1 відрізняється ("ab" "cde" відповідатиме "abc" "de"). Це означає, що це чудове рішення, якщо ви не можете використовувати динамічний тип, оскільки ви заздалегідь будуєте лямбда після групи в окремих виразах.
Брендон Барклі

3
"ab" "cde" насправді не повинен відповідати "abc" "de", отже, порожнє між ними.
Кай Хартманн

1
як щодо "abc de" "" та "abc" "de"?
AlbertK

@AlbertK це не працює, я думаю.
Кай Хартманн



1

Варто зазначити, що вам потрібно надіслати об’єкт для виразів Lambda і не можете використовувати екземпляр для класу.

Приклад:

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

    public string Prop2 { get; set; }
}

Це буде компілювати, але генеруватиме один ключ за цикл .

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

Якщо ви не хочете назвати ключові властивості, а потім відновити їх, ви можете зробити це так. Це GroupByправильно і надасть вам ключові властивості.

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

Для VB та анонімних / лямбда :

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.