VBA doesn't include a built-in function for searching an Array. Because of this, after doing work in VBA for a while, you'll probably have written several loops like this.
Dim i As Long
Dim found As Boolean
For i = 0 To UBound(a)
If a(i) = v Then
found = True
Exit For
End If
Next
If found Then
Debug.Print "Value was found at index " & i
Else
Debug.Print "Value is missing"
End If
You might think that this is quite a bit of code for what should be a simple operation. A huge element of good programming style is to eliminate repeated logic and abstract it into separate procedures. VBA, with its out of date, weirdly designed standard library, provides us with several opportunities to practice this aspect of software design.
I think that most VBA programmers should put together their own "utility" module that they include in any non-trivial projects to eliminate the standard, boilerplate code that they find themselves writing often. As you might have guessed by now, this post is going to focus on writing a standard ArraySearch function that would fit perfectly in such a utility module.
First attempt at ArraySearch
This is our first attempt. We're going to try just converting the code in the snippet above into a Function.
' Search the array [a] for value [x], returning whether x was found.
' If [found_index] is provided and [x] is found, [found_index] receives the
' index where [x] was found.
Public Function ArraySearch(a As Variant, x As Variant, _
Optional ByRef found_index As Variant) As Boolean
Dim i As Long
Dim result As Boolean
result = False
For i = LBound(a) To UBound(a)
If a(i) = x Then
result = True
found_index = i
Exit For
End If
Next
ArraySearch = result
End Function
I changed the indexing from the prior version to go from LBound to UBound in case someone passes in an array with a base other than zero, which is possible when declaring an array or by using the Option Base directive. Other than that, it's pretty similar.
As an exercise, try reading this function carefully and make a prediction of what kinds of input it will fail to process.
Let's do some unit testing, with a focus on VBA's many different possible kinds of arrays.
Private Sub TestArraySearchFixed()
Dim afixed(4) As String
Dim found As Long
afixed(0) = "alpha"
afixed(1) = "bravo"
afixed(2) = "1"
afixed(3) = "two"
afixed(4) = "3"
' ut1 successful/unsuccessful searches on fixed array
' unsuccessful search should preserve found_index
Assert ArraySearch(afixed, "two", found)
AssertEqual found, 3&
AssertFalse ArraySearch(afixed, 2, found)
AssertEqual found, 3&
End Sub
Private Sub TestArraySearchDynamic()
Dim adynamic() As Long
ReDim adynamic(3)
adynamic(0) = 78503
adynamic(1) = 65075
adynamic(2) = 21738
adynamic(3) = 43044
' ut2 unsuccessful/sucessful search on dynamic array
AssertFalse ArraySearch(adynamic, 44444)
Assert ArraySearch(adynamic, 78503)
End Sub
Private Sub TestArraySearchSpecial()
Dim abase(1 To 2) As Variant
Dim asingleton(0) As Integer
Dim aempty As Variant
Dim avar As Variant
abase(1) = "abc"
abase(2) = 123&
asingleton(0) = -1
aempty = Array()
' ut3 search on a singleton array (found and not found)
Assert ArraySearch(asingleton, -1)
AssertFalse ArraySearch(asingleton, -2)
' ut4 search on empty array
AssertFalse ArraySearch(aempty, vbNullString)
' ut5 search on homogeneous variant array (found and not found)
avar = Array(1, 2, 3, 4)
Assert ArraySearch2(avar, 3)
AssertFalse ArraySearch(avar, 0)
' ut6 searching non-array
AssertFalse ArraySearch("two", 2)
' ut7 searching for array within array
avar = Array(1, Array(2, 3), 4, 5)
Assert ArraySearch(avar, Array(2, 3))
End Sub
Unit tests 1 through 6 pass okay, but we run into problems on the last two.
Why did the tests ut6 and ut7 fail? How are we going to fix the function so that the tests pass? Could there be other problems with our ArraySearch function that we haven't even tested for yet? Find on Monday, in the next blog post!
No comments:
Post a Comment