【问题标题】:Filtering 2D Arrays in Excel VBA在 Excel VBA 中过滤二维数组
【发布时间】:2012-05-14 02:31:37
【问题描述】:

使用 Excel 和 VBA,我想要一些关于如何严格使用 VBA 最好地过滤数组中的数据的建议(就像使用数据透视表一样)。我正在创建一个用户窗体,它将根据当前现有数据做出一些数据决策。我可以想象如何做得足够好,但我并不精通 VBA 编程。

这是一个例子

A       B       C
bob     12      Small
sam     16      Large
sally   1346    Large
sam     13      Small
sally   65      Medium
bob     1       Medium

要获取数组中的数据,我可以使用

Dim my_array As Variant

my_array = Range("A1").CurrentRegion

现在,我熟悉循环遍历 2D 数组,但我想知道:过滤 2D 数组数据最有效的方法(无需一次又一次地循环遍历数组)?

例如,我如何获取会说获取这种数据:

data_for_sally As Variant 'rows with sally as name in ColA
data_for_sally_less_than_ten As Variant ' all rows with sally's name in ColA and colB < 10
data_for_all_mediums as Variant ' all rows where ColC is Medium

建议?我可以用一堆自定义函数和循环来解决这个问题,但我认为必须有更好的方法。谢谢。

【问题讨论】:

  • 请注意,第 4 个示例不是过滤器,而是对数组的操作,这可能会导致不同的答案。
  • 不确定在 VBA 中没有循环/自定义函数是否可行。您说您有其他语言的经验,您是否考虑过 VSTO/.NET 实现然后使用 LINQ?
  • 对于 VBA 中的这种类型的事情,我将使用断开连接的 ADO 记录集。它为您提供排序和过滤功能。
  • @assylias 好点,删除了那个例子; .@lori_m 实际上,我是按照 Python 的思路思考的; .@Tim Williams 我得研究一下,以前没听说过。

标签: arrays excel vba multidimensional-array filter


【解决方案1】:

试试这个

' credited to ndu
Function Filter2DArray(ByVal sArray, ByVal ColIndex As Long, ByVal FindStr As String, ByVal HasTitle As Boolean)
  Dim tmpArr, i As Long, j As Long, Arr, Dic, TmpStr, Tmp, Chk As Boolean, TmpVal As Double
  On Error Resume Next
  Set Dic = CreateObject("Scripting.Dictionary")
  tmpArr = sArray
  ColIndex = ColIndex + LBound(tmpArr, 2) - 1
  Chk = (InStr("><=", Left(FindStr, 1)) > 0)
  For i = LBound(tmpArr, 1) - HasTitle To UBound(tmpArr, 1)
    If Chk Then
      TmpVal = CDbl(tmpArr(i, ColIndex))
      If Evaluate(TmpVal & FindStr) Then Dic.Add i, ""
    Else
      If UCase(tmpArr(i, ColIndex)) Like UCase(FindStr) Then Dic.Add i, "" 'This finds only exact matches, if you need *FindStr* use:  If UCase(tmpArr(i, ColIndex)) Like UCase("*" & FindStr & "*") Then Dic.Add i, ""

    End If
  Next
  If Dic.Count > 0 Then
    Tmp = Dic.Keys
    ReDim Arr(LBound(tmpArr, 1) To UBound(Tmp) + LBound(tmpArr, 1) - HasTitle, LBound(tmpArr, 2) To UBound(tmpArr, 2))
    For i = LBound(tmpArr, 1) - HasTitle To UBound(Tmp) + LBound(tmpArr, 1) - HasTitle
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(i, j) = tmpArr(Tmp(i - LBound(tmpArr, 1) + HasTitle), j)
      Next
    Next
    If HasTitle Then
      For j = LBound(tmpArr, 2) To UBound(tmpArr, 2)
        Arr(LBound(tmpArr, 1), j) = tmpArr(LBound(tmpArr, 1), j)
      Next
    End If
  End If
  Filter2DArray = Arr
End Function

【讨论】:

    【解决方案2】:

    我假设您只想使用 VBA。

    我认为这取决于几个参数,主要是:

    • 您多久运行一次相同的条件 => 您是存储过滤器的结果还是每次都重新计算?
    • 您需要多久过滤一次内容 => 如果经常,则值得拥有适当的代码结构,如果不是,那么一次性循环显然是可行的方法。

    从面向对象的角度来看,假设性能(速度和内存)不是问题,我会选择以下设计(我不会详细介绍实现的细节,只给出大致的想法)。创建一个可以像这样使用的类(让我们想象一下 ArrayFilter)。

    设置过滤器

    Dim filter As New ArrayFilter
    With filter
        .name = "sam"
        .category = "Medium"
        .maxValue = 10
    End With
    

    或者

    filter.add(1, "sam") 'column 1
    filter.add(3, "Medium") 'column 3
    filter.addMax(2, 10) 'column 2
    

    创建过滤后的数据集

    filteredArray = getFilteredArray(originalArray, filter)
    

    getFilteredArray 写起来相当简单:循环遍历数组,检查值是否与过滤器匹配,然后将有效行放入一个新数组中:

    If filter.isValidLine(originalArray, lineNumber) Then 'append to new array
    

    优点

    • 简洁的设计
    • 可重复使用,尤其是在使用列号的第二个版本中。这可以用来过滤任何数组。
    • 过滤代码在一个可以测试的函数中
    • 推论:避免重复代码

    缺点

    • 每次都会重新计算过滤,即使您使用相同的过滤器两次。例如,您可以将结果存储在字典中 - 见下文。
    • 内存:每次调用 getFilteredArray 都会创建一个新数组,但不确定如何避免这种情况
    • 这会增加很多代码行,所以我只会在它有助于使代码更易于阅读/维护的情况下这样做。

    ps:如果您需要缓存结果以提高性能,一种方法是将结果存储在字典中,并在 getFilteredArray 函数中添加一些逻辑。请注意,除非您的数组非常大和/或您经常运行相同的过滤器,否则这可能不值得。

    filters.add filter, filteredArray 'filters is a dictionary
    

    这样,当你下次调用 getFilteredArray 时,你可以这样做:

    For each f in filters
        'Check if all conditions in f and newFilter are the same
        'If they are:
        getFilteredArray = filters(f)
        Exit Function
    Next
    
    'Not found in cache: compute the result
    

    【讨论】:

    • 这是一个有趣的方法——我希望 VBA 内置了一些我可以即时使用的东西。但是,重新使用 OO 方法可能必须是下一个最好的事情。感谢您的详细回复。必须尝试一下。
    • 再读一遍,getFilteredArray应该是ArrayFilter类里面的一个函数。
    • 什么是完整的OO?
    猜你喜欢
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-23
    • 2020-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多