Jvd*_*vdV 4 arrays excel vba unique filter
冒着成为话题的风险,我决定分享一些问答式的代码。如果一般意见认为这将是题外话,我很乐意在需要时删除。
背景
我们可以从任何一维数组或Range
变成一维数组的对象中检索所有唯一值,而不必遍历其元素吗?就我而言,普遍的共识是必须迭代不同的元素,最好的方法是使用字典或集合来存储唯一值。这是我发现非常有效的方法为了这个目的。
题
那么如何从一维数组中检索唯一元素,例如:
Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
Run Code Online (Sandbox Code Playgroud)
结果数组在哪里:
{"A", "C", "D", "E", "G"}
Run Code Online (Sandbox Code Playgroud)
真正需要的所有代码只是几行:
Sub test()
Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
With Application
uniques = .Index(arr, 1, Filter(.IfError(.Match(.Transpose(.Evaluate("ROW(1:" & UBound(.Match(arr, arr, 0)) & ")")), .Match(arr, arr, 0), 0), "|"), "|", False))
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
以上将返回一个一维数组,返回原始数组中的所有唯一元素:
说明:
检索所有这些值的行看起来很紧张,所以让我们把它分成几部分:
Application.Match
能够在其参数内处理数组。所以基本上我们正在看:.Match({"A","A","C","D","A","E","G"},{"A","A","C","D","A","E","G"},0)
。返回的数组将是: {1,1,3,4,1,6,7}
,这实际上是找到每个值的第一个位置。这一结果将是我们进一步发展的基础。
我们可以看到第三个.Match
在我们的代码,我们需要大致看出以下几点:.Match({1,2,3,4,5,6,7},{1,1,3,4,1,6,7},0)
。第一个参数是上面高亮代码检索到的内容。
从哪里.Evaluate("ROW(1:" & UBound(.Match(arr, arr, 0)) & ")")
返回一个值数组1-7
,Application.Transpose
将返回它,使其成为一维数组。
最后一步将返回一个包含错误的数组,但是代码不会中断,因为我们使用的是Application
代替WorksheetFunction
. 结果数组看起来像{1,Error 2042,3,4,Error 2042,6,7}
. 现在的重点是摆脱这些Error
价值观。
这样做的方法是通过Application.IfError
,它将评估数组并将所有错误值更改为给定的字符串值。在我们的例子中,我使用了管道符号。由用户决定一个足够独特的符号,它不会出现在原始数组的任何元素中。所以经过评价。我们目前的阵列将是这样的:{1,|,3,4,|,6,7}
。
现在我们检索了一个包含管道符号的数组,我们希望它们出来!一个快速的方法是使用Filter
函数。Filter
返回一个包含或不包含符合我们条件的元素的数组(取决于第三个参数中的TRUE
或FALSE
)。
所以基本上我们要返回一个数组,像这样:Filter(<array>, "|", False)
。得到的一维数组现在看起来像:{1,3,4,6,7}
。
我们在这一点上有点。我们只需要从原始数组中切出正确的值。为此,我们可以使用Application.Index
. 我们只想知道.Index
我们对哪些行感兴趣。为此我们可以加载我们之前找到的一维数组。所以代码看起来像:.Index(arr1, <array>, 1)
这将产生一个一维数组:{"A","C","D","E","G"}
结论:
你有它。一行(不仅仅是一个操作)从另一个一维数组中检索唯一值的一维数组,无需迭代。此代码已准备好用于任何用 声明的一维数组arr
。
有用吗?我不是 100% 确定,但我终于达到了我在项目中尝试的目标。生成的数组可以立即用于您需要在其中使用唯一值的任何任务。
比较:字典与应用程序。方法:
对 中的随机项目进行比较Range(A1:A50000)
,性能确实受到了打击。因此,迭代字典与Application.Methods
1000 个项目步骤中的非迭代方法之间的时间比较。低于 1000 项和每 10000 项标记的结果(以秒为单位):
| Items | Dictionary | Methods |
|------- |------------ |------------- |
| 1000 | 0,02 | 0,03 |
| 10000 | 0 | 0,88 |
| 20000 | 0,02 | 3,31 |
| 30000 | 0,02 | 7,3 |
| 40000 | 0,02 | 12,84 |
| 50000 | 0,03 | 20,2 |
Run Code Online (Sandbox Code Playgroud)
使用的Dictionary
方法:
Sub test()
Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
With Application
uniques = .Index(arr, 1, Filter(.IfError(.Match(.Transpose(.Evaluate("ROW(1:" & UBound(.Match(arr, arr, 0)) & ")")), .Match(arr, arr, 0), 0), "|"), "|", False))
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
结论:最多 1000 个项目,与更常见的Dictionary
做法相比,此方法的处理时间大致相同。在任何更大的事情上,迭代(通过内存)总是会击败方法方法!
我敢肯定,使用@ScottCraner所示的新动态数组函数,处理时间会受到更多限制。
使用新的动态数组函数,可以将其简化为:
Sub test()
Dim arr As Variant: arr = Array("A", "A", "C", "D", "A", "E", "G")
With Application
Dim uniques as variant
uniques = .Transpose(.Unique(.Transpose(arr)))
End With
End Sub
Run Code Online (Sandbox Code Playgroud)
新的Uniques Formula需要一个垂直数组,并且可以是2d。它的行为就像Range.RemoveDuplicate
无法选择列一样。
归档时间: |
|
查看次数: |
348 次 |
最近记录: |