[英]Matching values in string array
問題:尋找一種更有效的方法來查找1d數組中是否存在精確匹配值 - 實質上是布爾值true/false
。
我忽略了一些明顯的東西嗎 或者我只是使用錯誤的數據結構,當我可能應該使用集合對象或字典時使用數組? 在后者中,我可以分別檢查.Contains
或.Exists
方法
在Excel中,我可以檢查向量數組中的值,如:
If Not IsError(Application.Match(strSearch, varToSearch, False)) Then
' Do stuff
End If
這將返回一個完全匹配的索引,顯然受Match
函數的限制,該函數只能在此上下文中找到第一個匹配值。 這是一種常用的方法,也是我長期使用的方法。
這對Excel來說足夠令人滿意 - 但其他應用程序呢?
在其他應用程序中,我基本上可以做同樣的事情,但需要啟用Excel對象庫的引用,然后:
If Not IsError(Excel.Application.match(...))
但這看起來很愚蠢,並且由於權限/信任中心/等原因而難以管理分布式文件。
我試過使用Filter()函數:
If Not Ubound(Filter(varToSearch, strSearch)) = -1 Then
'do stuff
End If
但是這種方法的問題是Filter
返回部分匹配的數組,而不是完全匹配的數組。 (我不知道為什么返回子串/部分匹配會有用。)
另一種方法是逐字迭代數組中的每個值(我認為這也是非常常用的) - 這似乎比調用Excel的Match
函數更加麻煩。
For each v in vArray
If v = strSearch Then
' do stuff
End If
Next
如果我們要討論性能,那么運行一些測試就沒有任何代價。 根據我的經驗,Application.Match()比調用使用循環的函數慢十倍。
Sub Tester()
Dim i As Long, b, t
Dim arr(1 To 100) As String
For i = 1 To 100
arr(i) = "Value_" & i
Next i
t = Timer
For i = 1 To 100000
b = Contains(arr, "Value_50")
Next i
Debug.Print "Contains", Timer - t
t = Timer
For i = 1 To 100000
b = Application.Match(arr, "Value_50", False)
Next i
Debug.Print "Match", Timer - t
End Sub
Function Contains(arr, v) As Boolean
Dim rv As Boolean, lb As Long, ub As Long, i As Long
lb = LBound(arr)
ub = UBound(arr)
For i = lb To ub
If arr(i) = v Then
rv = True
Exit For
End If
Next i
Contains = rv
End Function
輸出:
Contains 0.8710938
Match 4.210938
我曾經尋找最好的替代解決方案。 它也適用於簡單的查找。
要查找字符串的第一個實例,您可以嘗試使用此代碼:
Sub find_strings_1()
Dim ArrayCh() As Variant
Dim rng As Range
Dim i As Integer
ArrayCh = Array("a", "b", "c")
With ActiveSheet.Cells
For i = LBound(ArrayCh) To UBound(ArrayCh)
Set rng = .Find(What:=ArrayCh(i), _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
MatchCase:=False)
Debug.Print rng.Address
Next i
End With
End Sub
如果要查找所有實例,請嘗試以下操作。
Sub find_strings_2()
Dim ArrayCh() As Variant
Dim c As Range
Dim firstAddress As String
Dim i As Integer
ArrayCh = Array("a", "b", "c") 'strings to lookup
With ActiveSheet.Cells
For i = LBound(ArrayCh) To UBound(ArrayCh)
Set c = .Find(What:=ArrayCh(i), LookAt:=xlPart, LookIn:=xlValues)
If Not c Is Nothing Then
firstAddress = c.Address 'used later to verify if looping over the same address
Do
'_____
'your code, where you do something with "c"
'which is a range variable,
'so you can for example get it's address:
Debug.Print ArrayCh(i) & " " & c.Address 'example
'_____
Set c = .FindNext(c)
Loop While Not c Is Nothing And c.Address <> firstAddress
End If
Next i
End With
End Sub
請記住,如果在一個單元格中有多個搜索字符串實例,則由於FindNext的特定,它將只返回一個結果。
盡管如此,如果你需要一個代碼來替換另一個找到的值,我會使用第一個解決方案,但你必須稍微改變一下。
“一種更有效的方法(與Application.Match
相比)查找數組中是否存在字符串值”:
我相信沒有比你正在使用的方法更有效的方法,即Application.Match
。
如果我們知道該元素的索引,則數組允許在任何元素中進行高效訪問。 如果我們想通過元素值做任何事情(甚至檢查元素是否存在),我們必須在最壞的情況下掃描數組的所有元素。 因此,最壞的情況需要n
元素比較,其中n
是數組的大小。 因此,我們需要查找元素是否存在的最大時間是輸入大小的線性,即O(n)
。 這適用於使用傳統陣列的任何語言。
我們可以更高效的唯一情況是陣列具有特殊結構。 對於您的示例,如果數組的元素是排序的(例如按字母順序排列),那么我們不需要掃描所有數組:我們與中間元素進行比較,然后與數組的左側或右側部分進行比較( 二進制搜索) ) 。 但沒有假設任何特殊結構,沒有希望..
您指向的Dictionary/Collection
提供對其元素的常量鍵訪問( O(1)
)。 可能還沒有很好地記錄的是,人們還可以對字典元素(鍵和項)進行索引訪問 :保留元素輸入Dictionary
的順序。 它們的主要缺點是它們使用更多內存,因為每個元素都存儲了兩個對象。
總結一下,雖然If Not IsError(Excel.Application.match(...))
看起來很愚蠢,但它仍然是更有效的方法(至少在理論上)。 在許可問題上,我的知識非常有限。 根據主機應用程序的不同,總會有一些Find
-type函數(例如C++
有find
和find_if
)。
我希望有所幫助!
編輯
在閱讀修改后的帖子和Tim的回答之后,我想補充一些想法。 上述文本側重於各種數據結構的理論時間復雜性,忽略了實現問題。 我認為問題的精神是相當“給予一定的數據結構(陣列)”,什么是檢查存在的實踐中最有效的方式。
為此,蒂姆的回答令人大開眼界。
傳統規則“如果VBA
可以為你做,那么不要自己再寫”並不總是如此。 循環和比較等簡單操作可以更快“同意” VBA
功能。 這里和這里有兩個有趣的鏈接。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.