簡體   English   中英

匹配字符串數組中的值

[英]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++findfind_if )。

我希望有所幫助!

編輯

在閱讀修改后的帖子和Tim的回答之后,我想補充一些想法。 上述文本側重於各種數據結構的理論時間復雜性,忽略了實現問題。 我認為問題的精神是相當“給予一定的數據結構(陣列)”,什么檢查存在的實踐中最有效的方式。

為此,蒂姆的回答令人大開眼界。

傳統規則“如果VBA可以為你做,那么不要自己再寫”並不總是如此。 循環和比較等簡單操作可以更快“同意” VBA功能。 這里這里有兩個有趣的鏈接。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM