Группировать по нескольким столбцам

Как я могу использовать 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

person Sreedhar    schedule 11.05.2009    source источник


Ответы (14)


Используйте анонимный тип.

Eg

group x by new { x.Column1, x.Column2 }
person leppie    schedule 11.05.2009
comment
Если вы новичок в группировке с анонимными типами, использование ключевого слова new в этом примере творит чудеса. - person Chris; 06.08.2013
comment
в случае mvc с ошибкой nHibernate для проблем с dll. Проблема решена с помощью GroupBy (x = ›new {x.Column1, x.Column2}, (key, group) =› new {Key1 = key.Column1, Key2 = key.Column2, Result = group.ToList ()}); - person Milan; 07.12.2015
comment
Я думал, что в этом случае новые объекты будут сравниваться по ссылке, поэтому нет совпадения - нет группировки. - person HoGo; 19.01.2016
comment
Анонимные типизированные объекты @HoGo, реализовать собственные методы Equals и GetHashCode, которые используются при группировке объектов. - person Byron Carasco; 07.09.2017
comment
Немного сложно визуализировать структуру выходных данных, когда вы новичок в Linq. Создает ли это группировку, в которой анонимный тип используется в качестве ключа? - person Jacques; 09.10.2017
comment
@Jacques да, но переводится правильно, например, в EF / Linq2Sql. - person leppie; 09.10.2017
comment
если вам нужен конкретный тип (например, новый Ttt {x.Column1, x.Column2}), используйте структуру для получения равных при сравнении составных частей - класс по умолчанию будет сравнивать ссылки, что, вероятно, не то, что вам нужно, поскольку каждый экземпляр будет другая ссылка - person Simon Dowdeswell; 11.12.2017
comment
Я просто хочу знать, влияет ли группа по нескольким столбцам на проблему с производительностью? Я столкнулся с проблемой после того, как применил группу с 4 столбцами. Запрос возвращает около 5000 записей, которые содержат несколько повторяющихся значений. - person Damith; 24.01.2018

Процедурный образец:

.GroupBy(x => new { x.Column1, x.Column2 })
person Mo0gles    schedule 07.02.2012
comment
Какой тип возвращается объект? - person mggSoft; 01.02.2013
comment
@MGG_Soft, который был бы анонимным типом - person Alex; 01.03.2013
comment
Этот код у меня не работает: Недопустимый декларатор анонимного типа. - person thalesfc; 07.07.2014
comment
@Tom, это должно работать так, как есть. Когда вы пропускаете присвоение имен полям анонимного типа, C # предполагает, что вы хотите использовать имя свойства / поля, к которому окончательно получили доступ, из проекции. (Итак, ваш пример эквивалентен Mo0gles) - person Chris Pfohl; 28.01.2015
comment
@Crisfole да, я полностью согласен, в большинстве случаев это правда. Однако бывают случаи, когда компилятор не может вывести имена полей, и они должны быть явно указаны. Например, когда вы получаете ошибку компиляции недопустимого декларатора анонимного типа. Это случилось со мной, а также с thalesfc, отсюда и комментарий. - person Tom Maher; 30.01.2015
comment
@Том. Я думаю, это применимо только в том случае, если вы рассчитываете значение, например. новый {x.Column1 + + x.Column2}, так как не будет имени для вывода. Простое использование имен свойств, как в примере выше, всегда должно работать. - person weenoid; 24.02.2015
comment
если я возвращаю это в своем методе, каким должен быть тип возвращаемого значения метода? предположим, что моя сущность - "MyEntity" - person Amir Chatrbahr; 16.03.2015
comment
нашел свой ответ. Мне нужно определить новый объект (MyViewEntity), содержащий свойства Column1 и Column2, и тип возвращаемого значения: IEnumerable ‹IGrouping‹ MyViewEntity, MyEntity ››, а фрагмент кода группировки: MyEntityList.GroupBy (myEntity = ›new MyViewEntity {Column1 = myEntity. Столбец1, Столбец2 = myEntity.Column2}); - person Amir Chatrbahr; 17.03.2015
comment
@AmirChatrbahr Это анонимный тип, поэтому нет явного типа для определения. Вот почему var был добавлен в .NET. var groupedStuff = stuff.GroupBy(x => new {x.Column1, x.Column2}); - person Andy_Vulhop; 16.04.2015
comment
@Andy_Vulhop, позволь мне прояснить ситуацию. моя проблема заключалась в ОБЪЯВЛЕНИИ метода, который не вызывает метод и не ИСПОЛЬЗУЕТ его. - person Amir Chatrbahr; 26.06.2015
comment
@AmirChatrbahr А, если вы возвращаете результат указанной группировки из метода, тогда да, это имеет смысл. Вы не можете определить возвращаемый тип метода как анонимный тип, поэтому ваше решение работает. Как правило, я использовал групповой запрос и проделал некоторую работу или сопоставил его с какой-либо моделью, поэтому вариант использования возврата IEnumerable<IGrouping<MyViewEntity, MyEntity>>, как вы, мне не приходил в голову. - person Andy_Vulhop; 29.06.2015

Хорошо, получил это как:

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();
person Sreedhar    schedule 11.05.2009
comment
+1 - Спасибо за исчерпывающий пример. Фрагменты другого ответа слишком короткие и не содержат контекста. Также вы показываете агрегатную функцию (в данном случае Sum). Очень полезно. Я считаю, что использование агрегатной функции (т. Е. MAX, MIN, SUM и т. Д.) Бок о бок с группировкой является обычным сценарием. - person barrypicker; 24.01.2014
comment
Здесь: stackoverflow.com/questions/14189537/, он отображается для таблицы данных, когда группировка основана на одном столбце, имя которого известно, но как это сделать, если столбцы, на основе которых группировка должна производиться динамически? - person b.g; 22.12.2016
comment
Это действительно полезно для понимания концепции группировки и применения к ней агрегирования. - person rajibdotnet; 19.09.2017
comment
Отличный пример ... именно то, что я искал. Мне даже был нужен агрегат, так что это был идеальный ответ, хотя я искал лямбду, я получил от него достаточно, чтобы удовлетворить свои потребности. - person Kevbo; 29.08.2018
comment
имеющий grp.Key. было то, что мне нужно, чтобы заставить его работать, спасибо! - person rob23; 15.01.2021

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

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

Таким же образом вы можете добавить Column3, Column4 и т. Д.

person Milan    schedule 30.12.2015
comment
Это было очень полезно и должно получить гораздо больше голосов! Result содержит все наборы данных, связанные со всеми столбцами. Большое спасибо! - person j00hi; 15.09.2016
comment
примечание: мне пришлось использовать .AsEnumerable () вместо ToList () - person GMan; 17.09.2016
comment
Отлично, спасибо за это. Вот мой пример. Обратите внимание, что GetFees возвращает 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 (); - person Craig B; 20.09.2016
comment
Можно ли получить из этого запроса другие столбцы, которые не были сгруппированы? Если есть массив объектов, я хотел бы сгруппировать этот объект по двум столбцам, но получить все свойства объекта, а не только эти два столбца. - person FrenkyB; 11.05.2018

Начиная с C # 7 вы также можете использовать кортежи значений:

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

or

.GroupBy(x => (x.Column1, x.Column2))
person Community    schedule 14.03.2017
comment
Ну, я думаю, вам не хватает дополнительного) в конце. Вы не закрываете () - person StuiterSlurf; 20.06.2017
comment
Я добавил это. - person Nathan Tregillus; 01.11.2017
comment
.GroupBy (x = ›новый {x.Column1, x.Column2}) - person Zahidul Islam Jamy; 18.07.2018

C # 7.1 или выше с использованием Tuples и Inferred tuple element names (в настоящее время он работает только с linq to objects и не поддерживается, когда требуются деревья выражений, например someIQueryable.GroupBy(...). Github issue):

// 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) });
person AlbertK    schedule 09.02.2019
comment
Ого, спасибо. Это оставило след в мозгу и увеличило способность к возможностям. - person Barry; 16.09.2020
comment
Супер ----- :) :) - person Thulasiram; 28.01.2021

Вы также можете использовать Tuple ‹> для строго типизированной группировки.

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),
}
person Jay Bienvenu    schedule 29.12.2015
comment
Примечание. Создание нового кортежа не поддерживается в Linq To Entities. - person foolmoron; 26.05.2016

Хотя этот вопрос касается свойств группировки по классам, если вы хотите сгруппировать по нескольким столбцам по объекту 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);
person Chris Smith    schedule 12.11.2013
comment
Я не мог выполнить группу запросов Linq c с помощью new {c.Field ‹String› (Title), c.Field ‹String› (CIF)}, и вы сэкономили мне много времени !! последний запрос был: группа c по новому {titulo = c.Field ‹String› (Title), cif = c.Field ‹String› (CIF)} - person netadictos; 15.08.2018

Следует отметить, что вам нужно отправить объект для лямбда-выражений и нельзя использовать экземпляр для класса.

Пример:

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;
}
person Ogglas    schedule 26.04.2018

сгруппировать x по новым {x.Col, x.Col}

person John    schedule 23.10.2017

Для VB и анонимного / лямбда:

query.GroupBy(Function(x) New With {Key x.Field1, Key x.Field2, Key x.FieldN })
person Dani    schedule 06.05.2020

person    schedule
comment
Ничего не добавляет к предыдущим ответам. - person Mike Fuchs; 09.02.2017

person    schedule
comment
В сочетании с Linq.Enumerable.Aggregate() это даже позволяет группировать по динамическому количеству свойств: propertyValues.Aggregate((current, next) => current + " " + next). - person Kai Hartmann; 01.08.2017
comment
Это лучший ответ, чем кто-либо думает. Это может быть проблематично, если могут быть экземпляры комбинаций, в которых добавление столбца 1 к столбцу 2 будет равняться тому же самому в ситуациях, когда столбец 1 отличается (ab cde будет соответствовать abc de). Тем не менее, это отличное решение, если вы не можете использовать динамический тип, потому что вы предварительно создаете лямбда-выражения после group by в отдельных выражениях. - person Brandon Barkley; 19.04.2018
comment
ab cde на самом деле не должно совпадать с abc de, отсюда пробел между ними. - person Kai Hartmann; 08.05.2018
comment
как насчет abc de и abc de? - person AlbertK; 15.03.2019
comment
@AlbertK Боюсь, это не сработает. - person Kai Hartmann; 30.11.2020

person    schedule
comment
Подумайте о добавлении объяснения того, как этот код решает проблему. - person Rosário Pereira Fernandes; 27.12.2017