匹配string数组中的值

问题:寻找一种更有效的方法来查找在1d数组中是否存在精确的匹配值 – 本质上是一个布尔值true/false

我可以忽略明显的东西吗? 或者我只是使用错误的数据结构,通过使用数组时,我可能应该使用集合对象或字典? 在后者中,我可以分别检查.Contains.Exists方法

在Excel中,我可以检查vector数组中的值,如:

 If Not IsError(Application.Match(strSearch, varToSearch, False)) Then ' Do stuff End If 

这将返回一个完全匹配的索引,显然受到Match函数的限制,它只能在这个上下文中find第一个匹配的值。 这是一种常用的方法,也是我长期以来使用的方法。

这对于Excel来说足够满意 – 但其他应用程序呢?

在其他应用程序中,我可以做基本相同的事情,但需要启用Excel对象库的引用,然后:

  If Not IsError(Excel.Application.match(...)) 

这似乎很愚蠢,而且由于权限/信任中心/等原因很难在分布式文件上pipe理。

我曾尝试使用Filter()函数:

  If Not Ubound(Filter(varToSearch, strSearch)) = -1 Then 'do stuff End If 

但是这个方法的问题是Filter返回一个部分匹配的数组,而不是一个完全匹配的数组。 (我不知道为什么这将是有用的返回子string/部分匹配。)

另一种方法是从字面上遍历数组中的每个值(这也是我认为非常常用的) – 这看起来比调用Excel的Match函数更加不必要的繁琐。

 For each v in vArray If v = strSearch Then ' do stuff End If Next 

如果我们要谈论性能,那么运行一些testing就没有必要。 根据我的经验,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 

我曾经寻找一个最好的替代解决scheme。 它也应该为简单的发现工作。

要find一个string的第一个实例,你可以尝试使用这个代码:

 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 

如果你想find所有的实例,请尝试下面的内容。

 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 

请记住,如果在一个单元格内有多个被searchstring的实例,由于FindNext的具体情况,它将只返回一个结果。

不过,如果你需要一个代码来replace另一个的值,我会使用第一个解决scheme,但是你必须改变一下。

“一个更有效的方法(比较Application.Match )查找一个string值是否存在于一个数组中”:

我相信没有比你使用的方法更有效的方法,即Application.Match

如果我们知道该元素的索引,数组允许在任何元素中进行有效的访问。 如果我们想通过元素值做任何事情(即使检查元素是否存在),我们也必须在最坏的情况下扫描数组中的所有元素。 因此,最坏的情况需要n元素比较,其中n是数组的大小。 因此,我们需要查找元素是否存在的最大时间与input大小(即O(n)线性关系。 这适用于使用传统arrays的任何语言。

唯一的情况是我们可以更有效率的时候,arrays有特殊的结构。 对于你的例子,如果数组的元素被sorting(例如按字母顺序),那么我们不需要扫描所有的数组:我们与中间元素进行比较,然后与数组的左边或右边进行比较( 二进制search) 。 但没有任何特殊的结构,没有希望

你所指的Dictionary/Collection ,提供了对其元素的持续的密钥访问( O(1) )。 也许没有很好的文档logging是,也可以有字典元素(键和项目)的索引访问 :元素进入Dictionary的顺序被保留。 他们的主要缺点是他们使用更多的内存,因为每个元素都存储了两个对象。

总之,尽pipeIf Not IsError(Excel.Application.match(...))看起来很愚蠢,但它仍然是更有效的方法(至less在理论上)。 在许可问题上,我的知识是非常有限的。 根据宿主应用程序,总是有一些Find -type函数(例如C++findfind_if )。

我希望有帮助!

编辑

在阅读这篇文章的修改版和Tim的回答之后,我想补充一些想法。 上面的文章集中在各种数据结构的理论时间复杂性上,忽略了实现问题。 我认为问题的精神是“给定一个数据结构(数组)”, 在实践中检查存在的最有效的方法是什么。

为此,蒂姆的回答让人大开眼界。

传统的规则“如果VBA可以为你做,那么不要再写一遍”并不总是如此。 循环和比较等简单操作可以更快地“同意” VBAfunction。 这里和这里有两个有趣的链接。