首页 > 解决方案 > 使用条件选择和汇总 DataTable 行

问题描述

我有这个数据表:

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 codeSeniority我需要检查列的值是否Rating相同Sector,如果相同,则合并这些行并对 and 的值Mkt Value求和Nom Value。相反,如果一个或两个不相同,我需要选择具有最高值的行Mkt Value(如果值相等,只需取 1 行)并丢弃该列中的其他行,但Mkt ValueNom Value仍然需要总和所有的行。

例如:对于BBG IPC code代码中的数字 117896,有不同的值,RatingSector需要具有最高值的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();

并且仍然是对丢弃的行求和的问题。谢谢你的帮助。

标签: c#linqdatatable

解决方案


一个问题是,当您调用 时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[]包含新数据的集合,然后将它们添加到结果表中,因为我们不能只创建一个DataRow没有a DataTable,所以我的回答做了三件事:

  1. DataTable使用所需的列创建一个新的
  2. 从原始表中选择合并的数据作为IEnumerable<object[]>
  3. 将每个object[]作为 a添加DataRowDataTable步骤 1

例如:

// 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分组才能正常工作)。

这是一个使用 a 的示例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();

推荐阅读