LinQ中Group by可否实现自定义扩展
-
2009年4月6日 6:26之前问了关于LinQ中Group by分组数据的问题
http://social.microsoft.com/Forums/zh-CN/vbasiczhchs/thread/494c0663-4580-4912-b30b-2eed3d9584ed
貌似这个问题比较棘手
那换个问题,LinQ中Group by可否通过System.Runtime.CompilerServices.Extension()来扩展呢
以期实现自定义的分组,
例如按不同日期段的分组,而不是特定日期的分组;
例如前文链接的问题要实现按两个列中的数据来分组
请高手指点一下
谢谢:)
编程是永无止境的,向大家学习- 已移动 feiyun0112MVP, Moderator 2009年4月7日 4:23 LINQ
全部回复
-
2009年4月9日 5:46版主你好,
你上次那个帖子我看过。的确非常有难度。
Linq中的group by是通过distinct()方法来实现的。
你上次想要的那个结果用SQL语句也不好实现。肯定要用到一些循环的。
所以我是这样测试的。
把日期先用distinct拿出来,然后用foreach循环取每个日期下的币种以及汇总。
但是当把每次查询到的结果放到一个集合中保存的时候遇到问题一直没解决。因为匿名类型的问题。
Microsoft Online Community Support -
2009年4月9日 9:42
你好,
distinct是用来排除重复记录的吧??
你上次那个帖子我看过。的确非常有难度。
Linq中的group by是通过distinct()方法来实现的。
你上次想要的那个结果用SQL语句也不好实现。肯定要用到一些循环的。
所以我是这样测试的。
把日期先用distinct拿出来,然后用foreach循环取每个日期下的币种以及汇总。
但是当把每次查询到的结果放到一个集合中保存的时候遇到问题一直没解决。因为匿名类型的问题。
Microsoft Online Community Support
我在 System.Linq命名空间里,找到了对Linq查询结果可使用的Groupby方法的定义
Public Shared Function GroupBy(Of TSource, TKey, TResult)(ByVal source As System.Collections.Generic.IEnumerable(Of TSource), ByVal keySelector As System.Func(Of TSource, TKey), ByVal resultSelector As System.Func(Of TKey, System.Collections.Generic.IEnumerable(Of TSource), TResult), ByVal comparer As System.Collections.Generic.IEqualityComparer(Of TKey)) As System.Collections.Generic.IEnumerable(Of TResult)
一共有8个这样的重载定义,
(这里有个题外的问题,就是我实在看不懂这些定义,定义中的两组括号是什么意思?)
思路一是自己重载一个GroupBy函数,然后对LinQ查询的结果使用这个自定义Groupby函数
思路二是直接写个函数自己分组
我试过这样写Linq语句Dim 单日记录 = From RowA As System.Data.DataRow In Table.Rows _ Where RowA.Item("日期") >= "#" & Strings.Format(日期, "M/d/yyyy 00:00:00#") AndAlso _ RowA.Item("日期") <= "#" & Strings.Format(日期, "M/d/yyyy 23:59:59#") Dim 枚举 = (From RowA As System.Data.DataRow In 单日记录 Where IsDBNull(RowA.Item("币种1")) = False Select 币种 = RowA.Item("币种1")).Union _ (From RowA As System.Data.DataRow In 单日记录 Where IsDBNull(RowA.Item("币种2")) = False Select 币种 = RowA.Item("币种2")) Dim 枚举结果 = 枚举.ToList Dim 分组 = From RowA In 单日记录 _ Group 币种 = 枚举结果 By 币种 = GroupTerm(枚举结果, RowA.Item("币种1"), RowA.Item("币种2")) _ Into 明细 = Group 'GroupTerm是自己写的一个函数,反映分组名跟记录中两列币种的关系 Private Function GroupTerm(ByVal List As System.Collections.Generic.List(Of Object), ByVal 币种1 As Object, ByVal 币种2 As Object) As UInteger If List.Contains(币种1) Then Return 币种1 ElseIf List.Contains(币种2) Then Return 币种2但是写好后发现这样还是达不到要求,就是因为LinQ的Group by不能重复分组记录,
End If End Function
当一条记录中既有“币种1”又有“币种2”而且“币种1”<>“币种2”时,Group by只能把记录分到其中一个组,不能同时分在两个组
所以,没办法,还是得自己实现Group by!
这也就是我发这个帖子的原因了。
重载Groupby的方法,我查阅了MSDN,有一段这样的代码,实现扩展LinQ中聚合函数,添加取中间值的median方法:
Imports System.Runtime.CompilerServices Imports System.Collections.Generic Imports System.Linq Imports System Module UserDefinedAggregates ' Calculate the median value for a collection of type Double. <Extension()> _ Function Median(ByVal medianAggregate As IEnumerable(Of Double)) As Double If medianAggregate.Count = 0 Then Throw New System.InvalidOperationException("Cannot compute median for an empty set.") End If Dim sortedList = From number In medianAggregate Order By number Dim medianValue As Double Dim itemIndex = CInt(Int(sortedList.Count / 2)) If sortedList.Count Mod 2 = 0 Then ' Even number of items in list. medianValue = ((sortedList(itemIndex) + sortedList(itemIndex - 1)) / 2) Else ' Odd number of items in list. medianValue = sortedList(itemIndex) End If Return medianValue End Function ' "Cast" the collection of generic items as type Double and call the ' Median() method to calculate the median value. <Extension()> _ Function Median(Of T)(ByVal medianAggregate As IEnumerable(Of T), _ ByVal selector As System.Func(Of T, Double)) As Double Return (From element In medianAggregate Select selector(element)).Median() End Function End Module
不过,回到刚才查到的GroupBy那个定义,似乎复杂了很多,要照样子画葫芦来重载GroupBy似乎……
还请高手帮忙了,呵呵
编程是永无止境的,向大家学习 -
2009年4月9日 9:56其实如果自己写循环,可能会简单点,但是对于代码本身来说会比较啰嗦,查询的次数也会比较多,代码运行的效率会受影响,总觉得不太满意:)
编程嘛,总是要不断追求完美的,对吧?
编程是永无止境的,向大家学习 -
2009年4月10日 15:04
自己写一个函数
function dt_0segments(dt as datetime) as int if dt >xxx then return 1 if dr <xxx and dt <yyy then return 2 ... ... .. end function 把这个加入group 如何?
如果还不行 就要考虑用 SP映射到实体 或者派生扩展一个字段
case when datefield beteen a and b then 1 when datefield beteen c and d then 2 end as group_mark
这样的语句
工作突然有点忙 嘿嘿- 已建议为答案 韦恩卑鄙 waywa 2009年7月6日 0:08
- 已标记为答案 冯瑞涛Moderator 2012年4月30日 3:12
-
2009年4月11日 1:09
自己写一个函数
function dt_0segments(dt as datetime) as int if dt >xxx then return 1 if dr <xxx and dt <yyy then return 2 ... ... .. end function 把这个加入group 如何?
如果还不行 就要考虑用 SP映射到实体 或者派生扩展一个字段
<pre lang=x-sql>case when datefield beteen a and b then 1 when datefield beteen c and d then 2 end as group_mark
这样的语句
这个方法我试过(就是上面说的GroupTerm函数),不过这里不能满足我的分组要求,
因为按我举的例子(http://social.microsoft.com/Forums/zh-CN/vbasiczhchs/thread/494c0663-4580-4912-b30b-2eed3d9584ed)的情况,分组的要求是从两个列中取值,当一条记录中列1=“币种1”,
列2=“币种2”
而且"币种1”<>"币种2”时,
Group by只能把记录分到其中一个组,即便我写了这样一个函数去判断,这个函数也不能返回2个分组名字给Group by语句吧?所以Group by不能同时把这行记录分在两个组里面,这就是现在遇到的问题了。
谢谢你的回答,请帮忙再看看,有解决办法吗?
:)
编程是永无止境的,向大家学习