使用字典检查多个列表的更优美的方式?

Nordicrev

这是一个早期阶段,因此我没有要共享的代码-但我想确保从一开始就使用正确的工具。我的项目是预算优化脚本。现在,我计划使用字典来存储预算名称并遍历每个预算名称,然后将费用ID与符合这些预算条件的费用列表进行比较。我不确定我是否打算使用正确的工具。

举个例子,如果我有医疗费用,我想从我的健康储蓄帐户HSA支付该费用。但是,如果我的HSA不够用,我将使用一般资金来支付。HSA将被定义为比一般项目具有更高优先级的资金。

仍然会针对HSA检查另一笔费用,例如一瓶波旁威士忌,因为它将从最高优先级到最低优先级循环遍历每个预算,但ID不在HSA的合格列表中,因此只能为使用普通资金(或两者之间的其他专门资金)。

复杂的是,预算的数量和名称不限于特定数量。我希望能够根据需求变化定义2或200个预算。符合条件的条件将在针对费用预算针对每个预算设置的查询中定义。(所以,类似

select expenseid from tblexpenses where category in ("MEDICAL","DENTAL")

适用于HSA,但另一个可能与另一个预算重叠,例如由于保险资金过剩而设置的预算。资格查询可能是:

select expenseid from tblexpenses where category = "DENTAL" and expensedate < to_date("2018-01-01","yyyy-mm-dd");

有一个表存储所有预算及其资格查询名称。我将使用它来填充预算列表,并在循环中用于测试支出。

我目前的总体计划是:

  1. 创建预算清单(词典)
  2. 从多个查询中创建资格列表(词典?),这些列表将生成符合条件的科目的支出清单。
  3. 评估费用,从最重要到最不重要
  4. 对于每个预算,比较每个费用ID(字符串)与资格
  5. 如果费用ID在预算的资格列表中,并且预算大于费用,则将其标记为由该预算资助,将所用预算减少该金额,然后继续。
  6. 如果没有,请测试下一个预算(转到5),直到没有更多预算为止。如果没有匹配项,则标记为未资助。
  7. 对每次费用都执行此操作,直到文件结束。
  8. 最后,报告剩余预算以及如何支付每笔费用。

我的问题是,对于存储构建多个列表,最优雅的方法是什么?我一直在考虑使用记录集,数组或字典直接查询表,到目前为止,看起来字典是赢家,但我不知道如何复制测试所需的查询,在SQL中,类似tblEligibility的SELECT count(1),其中WHERE预算= [BudgetID],而费用ID = [ExpenseID]。词典似乎能够对费用ID进行最后检查,但不能同时检查两者。

有什么建议吗?我在正确的轨道上吗?

编辑-tl:dr; 版:

我需要处理三个列表。一个是静态的,它是驱动循环的简单列表。第二个是具有两个数据元素的资格列表-预算ID和费用ID-我需要检查列表中是否存在组合对。最后是我需要随时修改的值。所有列表均由将存在于Access数据库中的表或查询填充。

保罗·比卡

嵌套字典可能没问题,但我会考虑构建一个更易于维护的自定义对象

对象VS嵌套词典


编辑

一个更具体的场景来说明数据流和对象使用情况
(不具有所有细节和要求,这就是我处理项目的方式)


预算工作表-从数据库/查询填充

预算案


费用表-从数据库/查询填充

花费


Module1 -Main Sub- pseudo-code(不起作用,只是高层结构)


Option Explicit

Public Sub SetBudgets()
    Dim wsB As Worksheet, wsE As Worksheet, budgets As clsBudget, itm As Range

    Set wsB = ThisWorkbook.Worksheets("Budgets")
    Set wsE = ThisWorkbook.Worksheets("Expense")
    Set budgets = New clsBudget

    Dim ok As Boolean, funded As Boolean, budget As clsBudget

    ok = True
    For Each itm In wsB.UsedRange.Rows      'Initialize Budgets
        With itm
            If ok Then ok = budgets.Init(.Cells(1), .Cells(2), .Cells(3), .Cells(4))
        End With
    Next

    funded = True
    Set budget = New clsBudget
    If ok Then
        For Each itm In wsE.UsedRange.Rows  'Commit Expenses
            For Each budget In budgets
                funded = budget.Commit(itm.Cells(4), itm.Cells(5))
                If funded Then
                    itm.Cells(7) = "Funded by budget " & budget.BudgetFullName
                    Exit For
                End If
            Next
            If Not funded Then itm.Cells(7) = "Unfunded"
        Next
    End If
End Sub

为了优化性能,将所有数据(两个工作表)都移到数组中


类实现- clsBudget - pseudo-code不工作,只是高层结构)


Option Explicit

Private Const ELIGIBILITY_LIST_DELIMITER = "||"

Private thisPriority    As Long     'Managed by the class
Private totalBudgets    As Long     'Managed by the class

Private priority        As Long     'Validated by the class
Private funds           As Double   '>= 0
Private fullName        As String   'Validated by the class (no special chars)
Private shortName       As String   'Validated by the class - extract initials
Private categories      As Dictionary   'No special chars, include other requirements...

Private Sub Class_Initialize()
    SetGlobals True
End Sub

Private Sub Class_Terminate()
    SetGlobals False
End Sub

Private Sub SetGlobals(Optional ByVal Init = False) 'reset all private variables
    thisPriority = 0
    totalBudgets = 0
    fullName = vbNullString
    '...
End Sub

Public Property Get BudgetFullName()  'define all accessor methods, and read-only props
    BudgetFullName = fullName
End Property

Public Function Init(ByVal budgetLonglName As Byte, ByVal budgetPriority As Long, _
       ByVal availableFunds As Double, ByVal eligibilityList As String) As Boolean

    If Len(budgetLonglName) = 0 Then Exit Function          'Return Error Details
    If budgetPriority <= currentPriority Then Exit Function 'Return Error Details
    If availableFunds <= 0 Then Exit Function               'Return Error Details
    If Len(eligibilityList) = 0 Then Exit Function          'Return Error Details

    fullName = budgetLonglName  'Remove special chars ("!@#$%^&*()_+{}|:<>?[]\;',./""")
    shortName = Split(fullName) 'For each itm extract first letter
    priority = budgetPriority
    funds = availableFunds

    Dim eList As Variant, cat As Variant
    eList = Split(eligibilityList, ELIGIBILITY_LIST_DELIMITER)

    For Each cat In eList
        'Remove special chars ("!@#$%^&*()_+{}|:<>?[]\;',./""")
        If Len(cat) > 0 Then categories(cat) = 0
    Next

    If categories.Count > 0 Then    'Budget is OK
        thisPriority = priority
        totalBudgets = totalBudgets + 1
        Init = True
    Else
        'Return Error Details
    End If
End Function

Private Function IsAvailable(ByVal category As String, _
                             ByVal expense As Currency) As Boolean

    If categories.Exists(category) Then IsAvailable = expense <= funds

End Function

Public Function Commit(ByVal category As String, _
                       ByVal expense As Currency) As Boolean
    If IsAvailable(category, expense) Then
        funds = funds - expense
        Commit = True
    End If
End Function

需要更多的逻辑来定义子类别(其他等)的资格映射,但是预算和费用之间的主要关系由类别字段驱动

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章