【问题标题】:How to Create a generic Iterator Extension Method with Predicate Function and Selector to Create a Special Grouping Method如何使用谓词函数和选择器创建通用迭代器扩展方法以创建特殊分组方法
【发布时间】:2019-12-31 20:09:33
【问题描述】:

我正在处理我们的销售部门仍在使用的旧的过时 4D 数据库。我创建了一个 ODBC 连接来读取表格,并一直在为我们的高管和经理创建可视化仪表板。

为了帮助理解.. 一个典型的数据库可能有一个 Order Table 和一个 OrderDetails 表。

此数据库有一个提案表和一个 [行项目] 表。此外,代替与给定 orderDetail 相关的可能的第三个子表(例如 OrderRelease),该数据库仅具有前面提到的 Singular [Line Item] 表,并且不存在将一个 lineitem 子级与父 lineitem 相关联的任何关系。 (一个 LineItem 可以有额外的 Line Items)

示例:

    | ProdCode |  Price  | FeatureNum | LineNum | SortOrder | NoFeature |
    |   ProdA  | 100,000 |            |    1    |     0     |   FALSE   |
    |   ProdA  |    0    | ProdA_SF1  |    2    |     1     |   FALSE   |        
    |   ProdA  |    0    | ProdA_SF2  |    3    |     1     |   FALSE   |   
    |   ProdA  |  5000   | ProdA_OF1  |    4    |     2     |   FALSE   |

现在.. 一个订单中可能有多个 ProductA,每个 ProductA 都有自己的特殊功能配置等。

查看 4D 中的原始代码,应用程序会逐行执行,并在以下情况下触发小计渲染:

  • 订单项的 [NoFeature] 为 True
  • 新的行项目记录已加载,并且该记录具有不同的产品代码
  • 正在评估最后一个订单项
  • 订单项的 [功能编号] 为空白,并且正在评估的订单项不是第一个订单项。

我要做的是设置一个迭代器扩展方法来处理这个自定义分组,并返回一个主行项目的 Ienumerable,并将它们的价格设置为呈现的小计。

这是我目前所拥有的:

  <Extension>
Public Iterator Function SpecialGrouping(Of T)(source As IEnumerable(Of T),
               ByVal predicate As Func(Of T, Integer, Boolean), ByVal Selector As Func(Of IEnumerable(Of T), T)) As IEnumerable(Of T)

    Dim start As Integer = 0

    While start < source.Count()
        Dim enumerable = source.Skip(start).TakeWhile(predicate)
        start += enumerable.Count
        Yield Selector(enumerable)
    End While

End Function

我不知道我的通用扩展方法实现有什么问题。我想这样称呼它:

Dim lineItems = GetLineItemsMethod ''for simplicity's sake
Dim subTotalRenderedMainItems = lineItems.SpecialGrouping(
    Function(x, index) Not (x.NoFeatures OrElse (x.LineNum <> 1 AndAlso x.FeatureNum = Nothing) OrElse index = 0),
    Function(x)
        Dim MainItem = x.OrderBy(Function(y) y.LineNum).FirstOrDefault
        MainItem.Price = x.Sum(Function(y) y.Price)
        Return MainItem
    End Function)

我们将不胜感激!

【问题讨论】:

    标签: vb.net lambda iterator extension-methods ienumerable


    【解决方案1】:

    您的方法非常复杂,因为它在评估期间重新枚举源。我会选择一种方法,您可以使用简单的For Each 进行迭代。

    <Extension>
    Public Iterator Function SpecialGrouping(Of T)(
        source As IEnumerable(Of T),
        isNewMainItem As Func(Of T, T, Boolean),
        initializeMainItem As Action(Of T),   'E.g. set price to 0.0
        aggregate As Action(Of T, T)   'E.g. add price of subitem (2nd param) to main item (1st param)
    ) As IEnumerable(Of T)
    
        Dim currentMainItem As T = Nothing
    
        For Each x As T In source
            If isNewMainItem(currentMainItem, x) Then
                If currentMainItem IsNot Nothing Then
                    Yield currentMainItem
                End If
                currentMainItem = x
                initializeMainItem(currentMainItem)
            ElseIf currentMainItem IsNot Nothing Then
                aggregate(currentMainItem, x)
            End If
        Next
        If currentMainItem IsNot Nothing Then
            Yield currentMainItem
        End If
    End Function
    

    该方法采用以下参数:

    • source 枚举 T
    • isNewMainItem 一个函数委托在新组开始时返回 True。第一个参数是当前的主项,第二个是被枚举的项。这使您可以比较两者的产品代码。
    • initializeMainItem。总结价格,你必须初始化总和,即主项目的Price,用0。
    • aggregate 将主项与子项组合在一起。

    例子

    Dim lineItems = GetLineItemsMethod()
    Dim subTotalRenderedMainItems = lineItems.SpecialGrouping(
        isNewMainItem:=Function(main, x) main Is Nothing OrElse main.ProdCode <> x.ProdCode _
            OrElse x.LineNum = 1,
        initializeMainItem:=Sub(x) x.Price = 0.0,
        aggregate:=Sub(main, x) main.Price = main.Price + x.Price
    )
    

    在第一次迭代期间,mainNothing。因此我们可以测试这个而不是测试 index=0。您可能需要微调 isNewMainItem 中的条件。

    如果某些项目不必汇总,则相应的逻辑将进入aggregate 委托。例如:

    aggregate:=Sub(main, x) main.Price = main.Price + If(x.NoFeatures, 0, x.Price) 
    

    【讨论】:

    • 这似乎可行,但需要注意的是..initializeMainItem..它将价格设置为零。 MainItem 行项目本身就是基本价格,并添加了所有可选功能。
    • 好的,如果您只是想在现有的主项目中添加子项目的价格,我们可以简单地删除initializeMainItem 参数。或者传递一个空参数作为参数initializeMainItem:=Sub(x) End Sub,其中End Sub 在下一行。
    • 我会编辑我原来的帖子来添加这个,但我觉得我最好解释一下。 NoFeatures 属性是一个布尔值,原始程序员使用它来将 subItem 分解为它自己的 Maintem。幸运的是,如果是这种情况,将不会有任何后续订单项仍属于先前的 mainitem..
    • 如果您将示例扩展为包含多个组会有所帮助。
    • isNewMainItem:=Function(main, x) main Is Nothing OrElse main.ProdCode x.ProdCode OrElse x.LineNum = 1 OrElse x.NoFeatures OrElse x.FeatureNum = Nothing 我相信这需要照顾它...让我测试一下
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多