Linq:获取具有多个嵌套组的内部对象的小计
Posted
技术标签:
【中文标题】Linq:获取具有多个嵌套组的内部对象的小计【英文标题】:Linq: Getting subtotals for the inner objects with multiple nested groups 【发布时间】:2021-01-09 18:23:17 【问题描述】:我需要根据从 SQL 存储过程中检索到的数据集创建一些报告。这是数据的表示:
+--------------+-------------------+-----------------+--------------------+
| CustomerName | CustInventoryAcct | ProductCategory | ProductSubCategory |
+--------------+-------------------+-----------------+--------------------+
| ABC | 12345 | Books | Fiction |
| ABC | 12345 | Books | Fiction |
| ABC | 12345 | Books | Non-Fiction |
| ABC | 67890 | Magazines | Sports |
+--------------+-------------------+-----------------+--------------------+
该数据集还包括许多与实际产品相关的数据字段,为简洁起见,我省略了这些字段 - 以上是我需要分组的四个标准。
报告需要对这四个字段中的每一个进行分组,因此使用上表,输出将如下所示:
Customer Header: ABC
Inventory Header: 12345
Category Header: Books
Sub Category Header: Fiction
Detail rows for each of the items in this group (2 using the sample data above)
Sub Category Footer: Fiction (Item Count : 2)
Sub Category Header: Non-Fiction
Detail row
Sub Category Footer: Non Fiction (Item Count: 1)
Category Footer: Books (Item Count: 3)
Inventory Footer: 12345 (Item Count: 3)
Inventory Header: 67890
Category Header: Magazines
Sub Category Header: Sports
Detail row
Sub Category Footer: Sports (Item Count: 1)
Category Footer: Magazines (Item Count: 1)
Inventory Footer: 67890 (Item Count: 1)
Customer Footer: ABC (Item Count: 4)
我有一个用于分组的 Linq 查询(不确定这是否是最好的方法):
var groupedData = rawData
.GroupBy(x => new x.ProductSubCategory, x.ProductCategory, x.CustInventoryAcct, x.Customer )
.GroupBy(x => new x.Key.ProductCategory, x.Key.CustInventoryAcct, x.Key.Customer )
.GroupBy(x => new x.Key.CustInventoryAcct, x.Key.Customer )
.GroupBy(x => new x.Key.Customer )
我使用嵌套的foreach
循环来构建报告结构:
foreach (var customer in groupedData)
GenerateHeader(customer.Key);
foreach (var custInventory in customer)
GenerateHeader(custInventory.Key.CustInventoryAcct);
foreach(var category in custInventory)
GenerateHeader(category.Key.ProductCategory);
foreach(var subCategory in category)
GenerateHeader(subCategory.Key.ProductSubCategory);
foreach (var item in subCategory)
// Populate detail rows
// Generate footer
// Generate footer
// Generate footer
// Generate footer
// Generate footer
这部分工作得很好——问题是当我试图获取每个页脚的项目数时。我可以使用Count()
获得最后一个嵌套级别(子类别)的项目计数,但如果我在更高级别尝试,它可以预见地给我该级别的组数,而不是底部的基础项目等级制度。我不知道如何向下钻取,以便为每个嵌套级别创建小计。
我可以通过使用一些int
计数器来解决这个问题,但我想知道在 Linq 中是否有办法做到这一点。我遇到的困难让我想知道我的分组查询是否不是在 Linq 中处理嵌套组的正确方法。
我已经阅读了很多关于使用 GroupBy
的 SO 答案和文章,但仍然无法弄清楚 - 有人可以帮助我了解完成我想做的事情的最佳方法吗?
【问题讨论】:
【参考方案1】:我建议不要预先做 4 个级别的 Group
,而是边做边做。这样你就可以Count()
每组给你一个小计:
foreach(var customer in rawData.GroupBy(x => new x.ProductSubCategory, x.ProductCategory, x.CustInventoryAcct, x.CustomerName ))
Console.WriteLine(customer.Key.CustomerName);
foreach(var inventory in customer.GroupBy(x => new x.ProductCategory, x.CustInventoryAcct, x.CustomerName ))
Console.WriteLine($"\tinventory.Key.CustInventoryAcct");
foreach(var category in inventory.GroupBy(x => new x.CustInventoryAcct, x.CustomerName ) )
// etc
Console.WriteLine($"\tNumber of items: inventory.Count()");
Console.WriteLine($"Number of items: customer.Count()");
现场示例:https://dotnetfiddle.net/YtShT3
【讨论】:
谢谢,不知道为什么我没有想到!这帮助我走上了正轨! 快速提问 - 你知道我将如何检查迭代是否在这些 foreach 循环中的最后一项上吗?我以前能够做类似if (category != inventory.Last())
的事情,但现在我不知道如何引用我正在迭代的集合。我想我可以使用常规的 for 循环,但如果你有一个巧妙的方法来做到这一点,我很乐意听到它!
@Jim 使用for
循环而不是foreach
,如果循环控制变量等于Count-1
,您就在最后一项。
感谢您的帮助!【参考方案2】:
尝试以下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication169
class Program
static void Main(string[] args)
DataTable dt = new DataTable();
dt.Columns.Add("CustomerName", typeof(string));
dt.Columns.Add("CustInventoryAcct", typeof(string));
dt.Columns.Add("ProductCategory", typeof(string));
dt.Columns.Add("ProductSubCategory", typeof(string));
dt.Rows.Add(new object[] "ABC", "12345", "Books", "Fiction" );
dt.Rows.Add(new object[] "ABC", "12345", "Books", "Fiction" );
dt.Rows.Add(new object[] "ABC", "12345", "Books", "Non-Fiction" );
dt.Rows.Add(new object[] "ABC", "67890", "Magazines", "Sports" );
foreach(var name in dt.AsEnumerable().GroupBy(x => x.Field<string>("CustomerName")))
Console.WriteLine("Customer Header: 0", name.Key);
foreach (var header in name.GroupBy(x => x.Field<string>("CustInventoryAcct")))
Console.WriteLine(" Inventory Header: 0", header.Key);
foreach (var category in name.GroupBy(x => x.Field<string>("ProductCategory")))
Console.WriteLine(" Category Header: 0", category.Key);
foreach (var subCategory in name.GroupBy(x => x.Field<string>("ProductSubCategory")))
Console.WriteLine(" Sub Category Header: 0", subCategory.Key);
foreach (DataRow row in subCategory)
Console.WriteLine(" Sub Category Footer: 0", subCategory.Key);
Console.WriteLine(" Category Footer: 0", category.Key);
Console.WriteLine(" Inventory Footer: 0", header.Key);
Console.WriteLine("Customer Footer: 0", name.Key);
Console.ReadLine();
【讨论】:
赞成,因为您有与 Jamiec 提出的相同概念 - 这个特定的答案对我不起作用,我在尝试使用 Field以上是关于Linq:获取具有多个嵌套组的内部对象的小计的主要内容,如果未能解决你的问题,请参考以下文章