在 linq 中使用数据行求和和分组依据

本文关键字:求和 linq 数据 | 更新日期: 2023-09-27 17:56:16

完全披露,我几乎是一个彻头彻尾的菜鸟。我可以基于我应该如何处理这个问题。

我有一个 3 列的DataTable

类,idate,数量

每个 ID 有多个日期,每个日期有多个金额。我需要做的是将每个 id 每天的金额相加,而不是:

  • ID,日期,金额
    • 00045,2011/02/13,11.50
    • 00045,2011/02/14,11.00
    • 00045,2011/02/14,12.00
    • 00045,2011/02/15,10.00
    • 00045,2011/02/15,5.00
    • 00045,2011/02/15,12.00
    • 00054,2011/02/13,8.00
    • 00054,2011/02/13,9.00

我会:

  • id,日期,金额总和
    • 00045,2011/02/13,11.50
    • 00045,2011/02/14,23.00
    • 00045,2011/02/15,27.00
    • 00054,2011/02/13,17.00

private void excelDaily_Copy_Into(DataTable copyFrom, DataTable copyTo)
{
    var results = from row in copyFrom.AsEnumerable()
    group row by new
    {
        oid = row["oid"],
        idate = row["idate"]
    } into n
    select new
    {
    ///unsure what to do
    }
};

我已经尝试了十几种不同的方法来做到这一点,我总是碰壁,我不知道如何进步。我一直在堆栈溢出和 msdn 上,到目前为止没有什么真正帮助我。

提前谢谢你!

在 linq 中使用数据行求和和分组依据

你可以试试这个:

var results = from row in copyFrom.AsEnumerable()
              group row by new
              {
                oid = row.Field<int>("oid"),// Or string, depending what is the real type of your column
                idate = row.Field<DateTime>("idate")
              } into g
              select new
              {
                 g.Key.oid,
                 g.Key.idate,
                 SumOfAmounts=g.Sum(e=>e.Field<decimal>("amount"));
              };

我建议使用 Field 扩展方法,该方法提供对指定行中每个列值的强类型访问。

虽然你没有指定它,但显然 copyFrom 是实现 IEnumerable 的类 DataTable 中的一个对象。

根据MSDN System.Data.DataTable,该类没有实现它。如果使用该类,则需要属性 Rows,该属性返回实现 IEnumerable 的行集合:

IEnumerable<DataRow> rows = copyFrom.Rows.Cast<DataRow>()

但是,如果您使用不同的 DataTable 类,则可能会执行类似操作以将其强制转换为 DataRow 序列。

类 System.Data.DataRow 的对象具有用于访问行中列的项属性。在您的情况下,列名是 oid、idate 和 amount。

要将副本从转换为要对其进行处理的项目序列,请执行以下操作:

var itemsToProcess = copyFrom.Rows.Cast<DataRow>()
    .Select(row => new
    {
        Oid = row["oid"],
        Date = (DateTime)row["idate"],
        Amount = (decimal)row["amount"],
    });

我不确定,但我假设列 idate 包含日期,列金额包含一些值。如果您的列包含其他类型,请随意使用其他类型。

如果列包含字符串,请使用 Parse 将它们转换为正确的项:

var itemsToProcess = copyFrom.Rows.Cast<DataRow>()
    .Select(row => new
    {
        Id = (string)row["oid"],
        Date = DateTime.Parse( (string) row["idate"]),
        Amount = Decimal.Parse (string) row["amount"]),
    });

如果您不熟悉 lambda 表达式。阅读以下内容对我有很大帮助:

itemsToProcess 是项的集合,取自 数据行,我们从此集合中的每一行创建了一个新的 具有三个属性的对象:Id = ...;数据 = ...;金额 = ...

  • 用于铸造和选择的标准 Linq 配置的说明
  • 匿名类型

现在我们有一个序列,我们可以在其中比较日期并对金额求和。

您想要的是将此序列中的所有项目分组到具有相同 ID 和日期的组中。因此,您需要一个

Id = 00045 且日期 = 02/13/2011 的组,以及一个 Id = 00045 且日期 = ,02/14/2011 的组。

为此,您可以使用Enumerable.GroupBy。作为选择器(= 一个组中的所有项目共有),您使用 ID 和日期的组合:

var groups = itemsToProcess.GroupBy(item => new
    {Id = item.Id, Data = item.Data} );

现在你有组了。

  • 每个组都有一个属性 Key,其类型具有两个属性:Id 和 Data。
  • 每个组都是 itemsToProcess 集合中的一系列项(因此它是具有 Id/数据/值属性的"itemToprocess")
  • 一个组中的所有项目都具有相同的 ID 和相同的数据。

因此,您所要做的就是对每个组中序列中的所有元素求和。

var resultSequence = groups.Select(groupItem => new
{
    Id = groupItem.Key.Id
    Date = groupItem.Key.Date,
    Sum = groupItem.Sum(itemToProcess => itemToProcess.Value,
}

所以把它们放在一个语句中:

var resultSequence = copyFrom.Rows.Cast<DataRow>()
    .Select(row => new
    {
        Id = (string)row["oid"],
        Date = DateTime.Parse( (string) row["idate"]),
        Amount = Decimal.Parse (string) row["amount"]),
    })
    .GroupBy (itemToProcess => new
    {
        Id = item.Id,
        Data = item.Data
    });
    .Select(groupItem => new
    {
        Id = groupItem.Key.Id
        Date = groupItem.Key.Date,
        Sum = groupItem.Sum(itemToProcess => itemToProcess.Value,
    });