使用Datarows

时间:2016-07-11 14:39:17

标签: c# linq

完全披露,我对linq来说几乎是一个完全的noob。我可以根据我应该如何接近这一点。

我有{3}的DataTable

的oid,IDATE,量

每个ID都有多个日期,每个日期都有多个金额。我需要做的是为每个id总计每天的金额,而不是:

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

我会:

  • ID,日期,SumOfAmounts
    • 00045,02 / 13 / 2011,11.50
    • 00045,02 / 14 / 2011,23.00
    • 00045,02 / 15 / 2011,27.00
    • 00054,02 / 13 / 2011,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,到目前为止没有任何东西真的帮助了我。

提前谢谢!

2 个答案:

答案 0 :(得分:2)

你可以试试这个:

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扩展方法,该方法提供对指定行中每个列值的强类型访问。

答案 1 :(得分:1)

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

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

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

但是如果你使用不同的DataTable类,你可能会做类似的事情,把它投射到一系列DataRow。

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

要将copyFrom转换为您要进行处理的项目序列,请执行以下操作:

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是一组项目,取自集合   DataRows,我们在这个集合的每一行创建了一个新的   具有三个属性的对象:Id = ...;数据= ......;金额= ...

现在我们有一个序列,我们可以比较日期和总和金额。

您想要的是将此序列中的所有项目分组到具有相同ID和日期的组中。所以你想要一个Id = 00045和Date = 02/13/2011的小组,以及一个Id = 00045和date =,02 / 14/2011的小组。

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

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

现在你有了小组。

  • 每个组都有一个属性Key,其类型包含两个属性:Id和Data。
  • 每个组 来自itemsToProcess集合的一系列项目(因此它是具有Id / Data / Value属性的“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,
    });