2016-07-11 21 views
2

完全公开,我几乎是一个总noob,当谈到linq。我可以基于我应该如何接近这一点。在linq中使用数据总和和分组

我有一个DataTable 3列

OID,IDATE,量

每个ID有多个日期,每个日期有多个数额。我需要做的是和每天每个ID的数量,所以不是:

  • 编号,日期,金额
    • 00045,02/13/2011,11.50
    • 00045, 2月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

我会:

  • 编号,日期,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

你可以试试这个:

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

+0

谢谢,这是我需要什么。所以,如果我想通过oid订购它,那么我会在选择新品之前做一个订单。 编辑:对不起,没有意识到它是迷你Markdown。 –

+0

一个简单的方法可以做到这一点:'result = result.OrderBy(e => new {e.oid,e.idate});' – octavioccl

1

虽然您没有指定它,但显然copyFrom是实现IEnumerable的DataTable类的对象。根据MSDN System.Data.DataTable该类不会实现它。如果您使用的类,你需要财产行,返回实现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包含日期和金额栏包含一些价值。如果您的列包含其他类型,请随意使用其他类型。

如果列包含字符串,并将其转换为使用解析正确的项目:

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和日期的组中。因此,您需要一个ID = 00045和日期= 02/13/2011的组,以及ID = 00045和日期= 2011年2月14日的组。

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

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

现在您有组。

  • 每个组都有一个属性Key,它具有两个属性:Id和Data。
  • 每组从itemsToProcess藏品序列(所以它是一个“itemToprocess”与ID /数据/ Value属性)
  • 在一个组中的所有项目都使用相同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, 
    }); 
+0

不是'AsEnumerable()'函数的目的是使DataTable '可访问? [链接](https://msdn.microsoft.com/en-us/library/system.data.datatableextensions.asenumerable(v = vs.110).aspx) 有没有一些好处,以铸造一个'DataTable '按照你指定的方式? 另外,感谢您的额外信息!我很难找到有用的linq教程,这些教程既不是特定的案例,也不是非常浅的解释。 –

+0

如果您不指定名称空间,则会发生这种情况。显然你谈论System.Data.DataTableExtensions.AsEnumerable()时,我正在谈论System.Linq.AsEnumerable,显然你的AsEnumerable返回一个EnumerableRowCollection实现IEnumerable < TRow >。如果您看到一些实现IEnumerable的集合类(通常相当古老),并且确信集合中的所有项都是MyType类型,则可以使用Cast < MyType >()(如我的示例中所示)将其设置为IEnumerable < MyType >(请参见System。 Linq.Enumerable.Cast) –