我有 2 个数据集,其中有很多字符串,我需要对其进行匹配。第一个是 1200 行,第二个大约 800 000 行。我通过 VBA 调用的 Excel 排序对这两个集合进行排序,以便它们按升序排列,因此我可以通过在最后一行后开始第二个数据集的每个下一个迭代来显着优化搜索速度匹配。
不幸的是,当没有找到匹配项时,Exit For
永远不会遇到,即使根据我的搜索词检查的字符串在字母表中更靠前(>我的字符串)。我尝试实现比较 If vData1(arrayIndex1, 1) < vData2(arrayIndex2, 1) Then
(也许之前使用“Mod”检查,如果在每次迭代中执行都会很慢),但我遇到不正确的比较值,例如 ?"µm">"zzzzz"
返回 true,而在数据集中,它应该是在以“a”开头的字符串之前。
有没有可靠的方法来解决这个问题?
Dim optimizedCounter as long, arrayIndex1 as long, arrayIndex2 as long
Dim vData1 as variant, vData2 as variant
'sort 2 data sets in Excel ascending
'assign data sets to arrays vData1 and vData2
optimizedCounter = LBound(vData2)
For arrayIndex1 = LBound(vData1) To UBound(vData1)
For arrayIndex2 = optimizedCounter To UBound(vData2)
If vData1(arrayIndex1, 1) = vData2(arrayIndex2, 1) Then
'do action when strings match
optimizedCounter = arrayIndex2 'narrow down 2nd data set's list, arrayIndex2 + 1 if vData1 has no duplicates
Exit For 'match has been found, exit loop and continue matching for next element in 1st data set
End If
Next arrayIndex2
Next arrayIndex1
编辑:
感谢大家提出的精彩建议。目前 A.S.H 的解决方案为 Application.Evaluate
/StrComp
为我成功了。因为我使用默认的二进制比较 vData1(arrayIndex1, 1) = vData2(arrayIndex2, 1)
,如果我想保留当前速度,则无法使用选项比较文本。
For arrayIndex1 = LBound(vData1) To UBound(vData1)
For arrayIndex2 = optimizedCounter To UBound(vData2)
If vData1(arrayIndex1, 1) = vData2(arrayIndex2, 1) Then
'do action when strings match
optimizedCounter = arrayIndex2 'narrow down 2nd data set's list, arrayIndex2 + 1 if vData1 has no duplicates
Exit For 'match has been found, exit loop and continue matching for next element in 1st data set
ElseIf arrayIndex2 Mod 1000 = 0 Then
If Application.Evaluate("""" & vData2(arrayIndex2, 1) & _
""" > """ & vData1(arrayIndex1, 1) & """") Then Exit For
'line below can be used instead of Application.Evaluate, the same speed, easier structure
'If StrComp(vData2(arrayIndex2, 1), vData1(arrayIndex1, 1), vbTextCompare) = 1 Then Exit For
End If
Next arrayIndex2
Next arrayIndex1
由于此方法需要一些时间,因此我被迫每 n 次迭代就使用它,以获得性能增益。根据数据集长度和匹配值的百分比,最佳 mod 值会有所不同。
作为对检查的组合数量的评论,我的搜索词列表包含重复项。
原版代码:
执行时间:12.76
处理的组合:144596591
Application.Evaluate 或 StrComp:
执行时间:17.30
处理的组合:1192341
条件 mod 50 = 0 下的 Application.Evaluate 或 StrComp:
执行时间:0.48
处理的组合:1201717
条件 mod 1000 = 0 下的 Application.Evaluate 或 StrComp:
执行时间:0.16
处理的组合:1376317
由于处理的组合数量较多,因此增加 mod 值将增加执行时间。
我尝试输入 With Application
在主循环之外并使用.Evaluate,它对速度完全没有影响。
编辑2:
Application.Match
和Application.Vlookup
不适用于行数 > 65536 的数组。然而,正如评论指出的那样,它们确实适用于范围。
Dim vMatch as Variant, myRng as Range
'myRng is set to one-column range of values to look for, about 800K rows
For arrayIndex1 = LBound(vData1) To UBound(vData1)
vMatch = Application.Match(vData1(arrayIndex1, 1), myRng, 0)
If Not IsError(vMatch) Then
'do action when strings match
End If
Next arrayIndex1
Application.Match 且 MatchType = 0:
执行时间:28.81
查找次数:1200
最佳答案
If vData1(arrayIndex1, 1) < vData2(arrayIndex2, 1) Then
... I encounter incorrect comparison values, for example?"µm">"zzzzz"
returns true, while in data set it is as it should be, before strings starting with "a".
事实上,如果之前的排序和你的代码中的字符串比较操作不同,那么之前的排序就变得毫无用处。发生这种情况是因为
VBA 中的比较默认为二进制
?"µm">"zzzzz" ---> True
?Application.Evaluate("""µm"">""zzzzz""") ---> False
?StrComp("µm", "zzzzz") ---> 1
?StrComp("µm", "zzzzz", vbTextCompare) ---> -1
^^^^^^^^^^^^^^
附:除非你设置Option Compare Text
或strComp
正如评论中指出的,或者通过使用 Excel 的比较:
If Application.Evaluate("""" & vData1(arrayIndex1, 1) & _
""" < """ & vData2(arrayIndex2, 1) & """") Then
这将解决比较不匹配的问题。确实根据 <
停止循环比较应该使它更快。这是否是最好的算法是另一个争论。您的数组正在排序,二分搜索应该是一个完美的候选者。
除非您使用二分搜索,否则请考虑使用 Excel 的内置函数,即 Application.VLookup
或Application.Match
,它们几乎比 VBA 循环快一个数量级,即使后者正在预取数组上工作。
关于arrays - VBA - 匹配2个排序字符串数组,其中某些元素不匹配 - 优化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43552170/