C#GroupBy-创建多个分组级别

C#GroupBy-创建多个分组级别,c#,linq,group-by,C#,Linq,Group By,鉴于以下类别: public class Transaction { public string Category { get; set; } public string Form { get; set; } } 如何获得按类别和表单分组的交易分组 基本上,我希望它像这样输出: Category 1 Form 1 Transaction1 Transaction2 Transaction3 ...

鉴于以下类别:

public class Transaction
{
    public string Category { get; set; }
    public string Form { get; set; }
}
如何获得按
类别
表单
分组的交易分组

基本上,我希望它像这样输出:

Category 1
    Form 1
        Transaction1
        Transaction2
        Transaction3
        ...
    Form 2
        Transaction1
        Transaction2
        Transaction3
        ...
Category 2
    Form 1
        Transaction1
        Transaction2
        Transaction3
        ...
    Form 2
        Transaction1
        Transaction2
        Transaction3
        ...

下面是一个使用嵌套foreach循环的示例,我不确定如何在一个linq语句字符串中实现这一点,可能有很多selectMany

var transactions = new[]{
    new{Category = "1", Form = "1", Title = "Trans1" },
    new{Category = "1", Form = "1", Title = "Trans2" },
    new{Category = "1", Form = "1", Title = "Trans3" },
    new{Category = "1", Form = "2", Title = "Trans1" },
    new{Category = "1", Form = "2", Title = "Trans2" },
    new{Category = "1", Form = "2", Title = "Trans3" },
    new{Category = "2", Form = "1", Title = "Trans1" },
    new{Category = "2", Form = "1", Title = "Trans2" },
    new{Category = "2", Form = "1", Title = "Trans3" },
    new{Category = "1", Form = "3", Title = "Trans1" },
    new{Category = "1", Form = "3", Title = "Trans2" },
    new{Category = "1", Form = "3", Title = "Trans3" },
};

foreach(var byCategory in transactions.GroupBy(x => x.Category))
{
    Console.WriteLine(byCategory.Key);
    foreach(var byForm in byCategory.GroupBy(x => x.Form))
    {
        Console.WriteLine("\t" + byForm.Key);
        foreach(var trans in byForm)
        {
            Console.WriteLine("\t\t" + trans.Title);
        }
    }
}
正因为我很好奇它看起来会是什么样子,所以我提出了以下建议,您不应该在生产代码中使用它,因为这很荒谬(如果您确实有这样的数据结构,那么应该将其分解为类似于字典或具有有意义类型的内容)

Dictionary ToomanyDictionary=事务
.GroupBy(x=>x.Category)
.ToDictionary(
catGroup=>catGroup.Key,
catGroup=>catGroup
.GroupBy(x=>x.Form)
.ToDictionary(
formGroup=>formGroup.Key,
formGroup=>formGroup.Select(x=>x.Title.ToList());

我以以下内容结束,因为在对集合进行迭代之前需要完成分组

为一些交易设定种子

var cats = new[] { "Category 1", "Category 2", "Category 3" };
var frms = new[] { "Form 1", "Form 2", "Form 3" };
var transactions = new List<Transaction>();

for (var i = 0; i <= 150; i++)
{
    transactions.Add(new Transaction
    {
        Category = i % 2 == 0 ? cats[0] : i % 3 == 0 ? cats[1] : cats[2],
        Form = i % 5 == 0 ? frms[0] : i % 7 == 0 ? frms[1] : frms[2]
    });
}
将其写入控制台

foreach (var group in groupedTransactions.OrderBy(x => x.Category))
{
    Console.WriteLine(group.Category);
    foreach (var form in group.Forms.OrderBy(x => x.Key))
    {
        Console.WriteLine("\t" + form.Key);
        foreach (var transaction in form)
        {
            Console.WriteLine("\t\t" + transaction.Id);
        }
    }
}

基本上,第一组应该包含以后需要分组的每一条信息,然后在重复分组时,删除与该分组级别相关的一条信息。你需要多少次就做多少次

ValueTuple
可以提供很多帮助,因为它允许您拥有一个可以传递给其他类型的复合键。否则,对于匿名类型,您需要依靠类型推断将组传递给其他对象。然而,
ValueTuple
的一个问题是,由于某种原因,您不能拥有1元组,因此在这种情况下,您需要按单个属性分组,而不使用元组

如果您的数据结构中已经存在层次关系,则可能不需要按
元组进行分组

var groups =
   transactions
   .GroupBy(tran => (
      Category: tran.Category,
      Form: tran.Form
   )).GroupBy(group => group.Key.Form)
   .ToList();
类型变得非常复杂,因此尽可能使用类型推断和重构工具来避免必须找出特定类型。例如,仅上述结果即为类型:

List<IGrouping<string, IGrouping<(string Category, string Form), Transaction>>>
列表

这是否回答了您的问题?
var groups =
   transactions
   .GroupBy(tran => (
      Category: tran.Category,
      Form: tran.Form
   )).GroupBy(group => group.Key.Form)
   .ToList();
List<IGrouping<string, IGrouping<(string Category, string Form), Transaction>>>