使用条件选择和汇总 DataTable 行
Posted
技术标签:
【中文标题】使用条件选择和汇总 DataTable 行【英文标题】:Select and sum DataTable rows with criteria 【发布时间】:2020-11-21 12:21:35 【问题描述】:我有这个数据表:
DataTable dt = new DataTable();
dt.Columns.Add("BBG IPC code", typeof(double));
dt.Columns.Add("Issuer Group", typeof(string));
dt.Columns.Add("Seniority", typeof(string));
dt.Columns.Add("Nom Value", typeof(double));
dt.Columns.Add("Mkt Value", typeof(double));
dt.Columns.Add("Rating", typeof(string));
dt.Columns.Add("Sector", typeof(string));
dt.Columns.Add("Analyst", typeof(string));
dt.Rows.Add(new object[] 117896, "Financiere", "Senior", 101, 20000.76, "BB", "Materials", "BAETZ" );
dt.Rows.Add(new object[] 117896, "Financiere", "Senior", 356, 300500, "BBB", "Materials", "BAETZ" );
dt.Rows.Add(new object[] 117896, "Financiere", "Senior", 356, 30000, "BBB", "Energy", "BAETZ" );
dt.Rows.Add(new object[] 117896, "Financiere", "Covered", 4888, 10000, "BB", "Energy", "BAETZ" );
dt.Rows.Add(new object[] 117896, "Financiere", "Covered", 645, 50000, "BBB", "Energy", "BAETZ" );
dt.Rows.Add(new object[] 117897, "Scentre Group", "Senior", 46452, 51066.5, "AA", "Energy", "BAETZ" );
dt.Rows.Add(new object[] 117898, "Vereniging Achmea", "Senior", 778, 90789.9, "C", "Insurance", "BAETZ" );
dt.Rows.Add(new object[] 117898, "Vereniging Achmea", "Senior", 7852, 10055.66, "C", "Utilities", "BAETZ" );
对于BBG IPC code
和Seniority
的每一对值,我需要检查Rating
和Sector
列的值是否相同,如果相同,则合并这些行并将@ 的值相加987654326@ 和Nom Value
。
相反,如果一个或两个不相同,我需要选择具有最高值的行Mkt Value
(如果值相等,只需取 1 行)并丢弃其他行但在列 Mkt Value
和Nom Value
我仍然需要所有行的总和。
例如:对于代码中的 BBG IPC code
数字 117896,Rating
和 Sector
的值不同,我需要最高值为 Mkt Value
的行(第二行 300500)并丢弃其他 2 Mkt Value
较低的行,但在丢弃它们之前,我需要将 300500+20000+30000 和 356+356+101 相加。
结果是 117896,"Financiere","Senior",813,350500,"BBB", "Materials", "BAETZ"
我已经尝试过类似的方法,但是有一个错误告诉我不能在 CopyToDataTable 中放入一个引用字段“Seniority”的字符串值...
DataTable maxIPC_Seniority = dt.AsEnumerable()
.OrderByDescending(x => x.Field<double>("Mkt Value"))
.GroupBy(x => x.Field<double>("IPC"), x => x.Field<string>("Seniority"))
.Select(x => x.FirstOrDefault())
.CopyToDataTable();
仍然是对丢弃的行求和的问题。谢谢你的帮助。
【问题讨论】:
您将"Seniority"
指定为元素选择器,因此您得到的只是string
字段的集合,因此.Select(x => x.FirstOrDefault())
选择的是string
,而不是@ 987654341@.
【参考方案1】:
一个问题是,当您调用GroupBy
时,您将"IPC"
列设置为Key
选择器,但表中没有"IPC"
列。相反,您应该使用实际的列名"BBG IPC code"
。
下一个问题是你调用了GroupBy
的重载,它将一个键选择器作为第一个参数,一个元素选择器作为第二个参数,所以它只是选择组中的"Seniority"
列。
相反,要按两列作为键进行分组,我们需要为 Key
创建一个新的匿名对象,其中包含具有列值的属性:
var maxIPC_Seniority = dt.AsEnumerable()
.OrderByDescending(row => row.Field<double>("Mkt Value"))
.GroupBy(row =>
new
IPC = row.Field<double>("BBG IPC code"),
Seniority = row.Field<string>("Seniority")
)
.Select(group => group.FirstOrDefault())
.CopyToDataTable();
现在,要按照您的意愿组合行,我认为唯一的方法是使用新数据选择 object[]
的集合,然后将它们添加到结果表中,因为我们可以'不只是创建一个没有DataTable
的DataRow
,所以我的回答做了三件事:
-
使用所需的列创建一个新的
DataTable
从原始表中选择合并的数据作为IEnumerable<object[]>
将每个object[]
作为DataRow
添加到步骤1 中的DataTable
例如:
// Create a new DataTable with the same columns as `dt`
DataTable maxIpcSeniority = dt.Clone();
// Group our set of original data, do the merging of rows as necessary
// and then return the row data as a list of object[]
var maxIpcSeniorityRowData = dt.AsEnumerable()
.OrderByDescending(row => row.Field<double>("Mkt Value"))
.GroupBy(row =>
new
IPC = row.Field<double>("BBG IPC code"),
Seniority = row.Field<string>("Seniority")
)
.Select(group =>
// Since the data is ordered by MktValue already, we can just grab
// the first one to use for filling in the non-merged fields
var firstRow = group.First();
return new object[]
group.Key.IPC,
firstRow.Field<string>("Issuer Group"),
group.Key.Seniority,
group.Sum(row => row.Field<double>("Nom Value")),
group.Sum(row => row.Field<double>("Mkt Value")),
firstRow.Field<string>("Rating"),
firstRow.Field<string>("Sector"),
firstRow.Field<string>("Analyst")
;
)
.ToList();
// Add each set of rowData to our new table
foreach (var rowData in maxIpcSeniorityRowData)
maxIpcSeniority.Rows.Add(rowData);
如果由于某种原因您不能使用花括号,您可以使用Tuple
(甚至创建一个单独的类)来存储GroupBy
字段而不是匿名类型。这样,您可以通过构造函数添加值,而不是在花括号中初始化属性。 (请注意,如果您确实创建了一个类来执行此操作,则需要覆盖 Equals
和 GetHashCode
才能使分组正常工作)。
这是一个使用 Tuple<double, string>
的示例:
var maxIpcSeniorityRowData = dt.AsEnumerable()
.OrderByDescending(row => row.Field<double>("Mkt Value"))
.GroupBy(row => new Tuple<double, string>(
row.Field<double>("BBG IPC code"),
row.Field<string>("Seniority")))
.Select(group =>
var firstRow = group.First();
return new object[]
group.Key.Item1,
firstRow.Field<string>("Issuer Group"),
group.Key.Item2,
group.Sum(row => row.Field<double>("Nom Value")),
group.Sum(row => row.Field<double>("Mkt Value")),
firstRow.Field<string>("Rating"),
firstRow.Field<string>("Sector"),
firstRow.Field<string>("Analyst")
;
)
.ToList();
【讨论】:
谢谢,它在 VisualStudio 上运行良好,但不幸的是,我必须在一些调用代码中运行此代码,并且由于代码中 lambda 表达式的大括号而无法运行。它可以做同样的事情,但没有他们?例如这不起作用... new IPC = row.FieldTuple
(甚至创建一个单独的类)来存储GroupBy
字段而不是匿名类型,并通过构造函数添加值。我更新了示例(在底部)。以上是关于使用条件选择和汇总 DataTable 行的主要内容,如果未能解决你的问题,请参考以下文章