【问题标题】:Group By Multiple Columns按多列分组
【发布时间】:2010-10-25 05:27:24
【问题描述】:

如何在 LINQ 中对多列进行 GroupBy 操作

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

【问题讨论】:

    标签: c# .net linq group-by aggregate


    【解决方案1】:

    使用匿名类型。

    例如

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

    【讨论】:

    • 如果您不熟悉使用匿名类型进行分组,则在本示例中使用“new”关键字会很神奇。
    • 如果 mvc 与 nHibernate 出现 dll 问题错误。 GroupBy(x=> new { x.Column1, x.Column2 }, (key, group) => new { Key1 = key.Column1, Key2 = key.Column2 , Result = group.ToList() });
    • 我认为在这种情况下,新对象将通过引用进行比较,因此不匹配 - 不分组。
    • @HoGo 匿名类型对象implement their own Equals and GetHashCode 用于对对象进行分组的方法。
    • 对于 Linq 新手来说,可视化输出数据结构有点困难。这会创建一个使用匿名类型作为键的分组吗?
    【解决方案2】:

    程序示例:

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

    【讨论】:

    • 返回的对象是什么类型的?
    • @MGG_Soft 这将是一个匿名类型
    • @Tom 这应该可以正常工作。当您跳过命名匿名类型的字段时,C# 假定您要使用投影中最终访问的属性/字段的名称。 (所以你的例子相当于 Mo0gles')
    • @Crisfole 是的,我完全同意在大多数情况下这是真的。但是,有时编译器无法推断字段名称,必须明确指定它们。就像您收到“无效的匿名类型声明符”编译错误一样。它发生在我身上,也发生在 thalesfc 身上,因此发表了评论。
    • 找到了我的答案。我需要定义一个包含 Column1 和 Column2 属性的新实体(MyViewEntity),返回类型是:IEnumerable> 和分组代码片段是:MyEntityList.GroupBy(myEntity => new MyViewEntity { Column1 = myEntity. Column1, Column2 = myEntity.Column2 });
    【解决方案3】:

    好的,如下:

    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();
    

    【讨论】:

    • +1 - 感谢您提供全面的示例。另一个答案的 sn-ps 太短且没有上下文。您还显示了一个聚合函数(在这种情况下为 Sum)。非常有帮助。我发现将聚合函数(即 MAX、MIN、SUM 等)与分组并排使用是一种常见的场景。
    • 这里:stackoverflow.com/questions/14189537/…,当分组基于单个列时,它显示为一个数据表,其名称是已知的,但是如果分组所基于的列如何做到这一点必须动态生成吗?
    • 这对于理解分组概念和对其应用聚合非常有帮助。
    • 很好的例子......正是我想要的。我什至需要聚合,所以这是一个完美的答案,即使我正在寻找 lambda,我从中得到了足够的东西来解决我的需求。
    • 拥有 grp.Key。是我让它工作所需要的,谢谢!
    【解决方案4】:

    对于按多列分组,试试这个...

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

    同样的方法可以添加 Column3、Column4 等。

    【讨论】:

    • 这很有帮助,应该会得到更多的支持! Result 包含链接到所有列的所有数据集。非常感谢!
    • 注意:我必须使用 .AsEnumerable() 而不是 ToList()
    • 太棒了,谢谢。这是我的例子。请注意,GetFees 返回 IQueryable 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();
    • 是否可以从此查询中获取未分组的其他列?如果有对象数组,我想将此对象按两列分组,但从对象中获取所有属性,而不仅仅是这两列。
    【解决方案5】:

    从 C# 7 开始,您还可以使用值元组:

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

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

    【讨论】:

    • 好吧,我认为您最后缺少了一个额外的 ) 。你没有关闭 ()
    • 我已经添加了。
    • .GroupBy(x => new { x.Column1, x.Column2})
    【解决方案6】:

    C# 7.1 或更高版本 使用TuplesInferred 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) });
    

    【讨论】:

    • 哇,谢谢。这在大脑中留下了印记,并增加了可能性。
    • 超级 ----- :) :)
    【解决方案7】:

    您也可以使用元组进行强类型分组。

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

    【讨论】:

    • 注意: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);
    

    【讨论】:

    • 我无法执行 Linq 查询 "group c by new{c.Field("Title"),c.Field("CIF")}",而你节省了我很多时间!!最后的查询是:“group c by new{titulo= c.Field("Title"),cif=c.Field("CIF")} "
    【解决方案9】:
    var Results= query.GroupBy(f => new { /* add members here */  });
    

    【讨论】:

    • 对前面的答案没有添加任何内容。
    【解决方案10】:

    需要注意的是,您需要为 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;
    }
    

    【讨论】:

      【解决方案11】:
      .GroupBy(x => x.Column1 + " " + x.Column2)
      

      【讨论】:

      • 结合Linq.Enumerable.Aggregate() 这甚至允许按动态数量的属性进行分组:propertyValues.Aggregate((current, next) =&gt; current + " " + next)
      • 这是一个比任何人都认为的更好的答案。如果在 column1 不同的情况下(“ab”“cde”将匹配“abc”“de”),添加到 column2 的 column1 可能等于相同的组合实例,则可能会出现问题。也就是说,如果您不能使用动态类型,这是一个很好的解决方案,因为您在单独的表达式中在 group by 之后预先构造 lambda。
      • "ab" "cde" 实际上不应该匹配 "abc" "de",因此中间有空格。
      • “abc de”“”和“abc”“de”呢?
      【解决方案12】:

      将 x 按新 { x.Col, x.Col} 分组

      【讨论】:

        【解决方案13】:

        .GroupBy(x =&gt; (x.MaterialID, x.ProductID))

        【讨论】:

        • 考虑添加关于此代码如何解决问题的说明。
        【解决方案14】:

        对于 VB匿名/lambda

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

        【讨论】:

          猜你喜欢
          • 2015-12-02
          • 2017-02-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-22
          • 2016-09-25
          相关资源
          最近更新 更多