LinQ中Group by可否实现自定义扩展

已答复 LinQ中Group by可否实现自定义扩展

全部回复

  • 2009年4月9日 5:46
    版主
     
     
    你好,

    你上次那个帖子我看过。的确非常有难度。

    Linq中的group by是通过distinct()方法来实现的。

    你上次想要的那个结果用SQL语句也不好实现。肯定要用到一些循环的。
    所以我是这样测试的。

    把日期先用distinct拿出来,然后用foreach循环取每个日期下的币种以及汇总。
    但是当把每次查询到的结果放到一个集合中保存的时候遇到问题一直没解决。因为匿名类型的问题。
    Microsoft Online Community Support
  • 2009年4月9日 9:42
     
      包含代码
    你好,

    你上次那个帖子我看过。的确非常有难度。

    Linq中的group by是通过distinct()方法来实现的。

    你上次想要的那个结果用SQL语句也不好实现。肯定要用到一些循环的。
    所以我是这样测试的。

    把日期先用distinct拿出来,然后用foreach循环取每个日期下的币种以及汇总。
    但是当把每次查询到的结果放到一个集合中保存的时候遇到问题一直没解决。因为匿名类型的问题。
    Microsoft Online Community Support
    distinct是用来排除重复记录的吧??

    我在 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
    End If End Function
    但是写好后发现这样还是达不到要求,就是因为LinQ的Group by不能重复分组记录,
    当一条记录中既有“币种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

    这样的语句



    工作突然有点忙 嘿嘿
  • 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不能同时把这行记录分在两个组里面,这就是现在遇到的问题了。
    谢谢你的回答,请帮忙再看看,有解决办法吗?
    :)
    编程是永无止境的,向大家学习