Visual Basic 2005 makes it very easy to pass arrays and
collections into and out of methods. This makes arrays, collections, and
similar objects very useful for efficiently grouping data. Additionally,
there are some new and useful methods for processing arrays that are
easy to overlook if you’re just moving up from Visual Basic 6.0. Several
recipes in this chapter focus on these methods. For example, arrays have
a built-in Sort()
method that will sort some or all of the
elements in the array, a feature that had to be coded by hand before
.NET.
Generics are also new in Visual Basic 2005, providing a powerful new type-safe way to define collections and other objects such as lists, stacks, and queues. Generics enable compile-time typing of objects without your having to write separate classes for each type you want to support. This chapter demonstrates a simple generic collection. Other chapters provide further examples of generics.
You want to fill an array with starting values without having to explicitly assign each array element individually.
You can load an array in the
Dim
statement using empty parentheses after
either the array’s name or its type designation, followed by braces
listing the array elements to be assigned.
The following line of code creates a one-dimensional array of integers with three elements (elements 0 through 2):
Dim array1D( ) As Integer = {1, 2, 3}
A two-dimensional array is only slightly trickier to fill on the spot, requiring nested braces containing the array elements. You can put the nested braces all on one line, or you can use the underscore line-continuation symbol to format the data in a more readable layout, such as in the following example:
Dim array2D(,) As Integer = { _ {1, 2}, _ {3, 4}}
For comparison, the following line of code creates exactly the same array:
Dim array2D(,) As Integer = {{1, 2}, {3, 4}}
Arrays with three or more dimensions are declared in a similar way, with additional commas and curly braces included as needed:
Dim array3D(,,) As Integer = _ {{{1, 2}, {3, 4}}, {{5, 6}, {7, 8}}}
For comparison, the following block of code creates exactly the same three-dimensional array and fills each element with the same values, but does so using a more traditional method of assigning each individual element:
Dim array3D(1, 1, 1) As Integer array3D(0, 0, 0) = 1 array3D(0, 0, 1) = 2 array3D(0, 1, 0) = 3 array3D(0, 1, 1) = 4 array3D(1, 0, 0) = 5 array3D(1, 0, 1) = 6 array3D(1, 1, 0) = 7 array3D(1, 1, 1) = 8
You want to sort the elements of an array.
The Array
class has a shared
Sort()
method that works on arrays
of any kind. There are several optional parameters that let you
customize the sorting algorithm for different types of objects, but
for arrays of strings and numbers, the name of the array is generally
all you need to pass. The following example creates a string array
containing the names of a few types of fruit, then sorts them into
alphabetical order and displays the sorted list of fruit names for
review:
Dim result As New System.Text.StringBuilder Dim arrayToSort( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} ' ----- Show the elements before sorting. result.AppendLine("Before sorting:") For Each fruit As String In arrayToSort result.AppendLine(fruit) Next fruit ' ----- Show the elements after sorting. result.AppendLine( ) result.AppendLine("After sorting:") Array.Sort(arrayToSort) For Each fruit As String In arrayToSort result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
The StringBuilder
is first
filled with the names of the fruits in the unsorted order used to
create the string array. The Array.Sort()
method is invoked to sort the
fruits alphabetically, and the sorted fruits are then added to the
StringBuilder
to demonstrate the
sorted order. Figure 8-1
shows the array before and after the sort.
Sorting intrinsic types is simple, but you can also
sort custom classes based on any comparison criteria you specify. You
do this by implementing the IComparable
interface on the custom class.
The following class implements a simple comparison interface that
merges group and item values into a single string for
comparison:
Private Class CustomData Implements IComparable Public GroupName As String Public ItemName As String Public Sub New(ByVal theGroup As String, _ ByVal theItem As String) GroupName = theGroup ItemName = theItem End Sub Public Overrides Function ToString( ) As String Return GroupName & ": " & ItemName End Function Public Function CompareTo(ByVal obj As Object) As Integer _ Implements System.IComparable.CompareTo ' ----- Compare two records. Dim compareValue As String ' ----- Since we're just going to compare the ToString ' value, no need to convert to CustomData. compareValue = obj.ToString( ) ' ----- Return the relative comparison value. Return String.Compare(Me.ToString( ), compareValue) End Function End Class
The CompareTo()
method
returns a negative value if the object itself should come before
another object supplied for comparison, a positive value if the
instance should come after, and zero if they are equal. The String
object’s comparer was deferred to
here, but you can use any complex calculations for comparison.
The following sample sorts an array of CustomData
data elements:
Dim result As New System.Text.StringBuilder Dim arrayToSort( ) As CustomData = { _ New CustomData("Fruit", "Orange"), _ New CustomData("Vegetable", "Onion"), _ New CustomData("Fruit", "Apple"), _ New CustomData("Vegetable", "Carrot"), _ New CustomData("Fruit", "Grape")} ' ----- Show the elements before sorting. result.AppendLine("Before sorting:") For Each food As CustomData In arrayToSort result.AppendLine(food.ToString( )) Next food ' ----- Show the elements after sorting. result.AppendLine( ) result.AppendLine("After sorting:") Array. Sort(arrayToSort) For Each food As CustomData In arrayToSort result.AppendLine(food.ToString( )) Next food MsgBox(result.ToString( ))
Figure 8-2 shows the output from this code.
Recipe 8.3 shows how to reverse the elements of an array, and Recipe 8.5 shows how to randomly rearrange the elements of an array.
You want to reverse the order of the elements in an array. This might be useful, for instance, immediately after sorting an array to change the sort order from ascending to descending.
Sample code folder: Chapter 08ArrayReversal
The Array
class provides a
shared Reverse()
method that
reverses the order of its elements.
The Array.Reverse()
method
reverses an array, whether its elements have been sorted first or not.
The following code fills a string array with a few fruit names, in no
special order. The Array.Reverse()
method then reverses the order of the array’s elements:
Dim result As New System.Text.StringBuilder Dim arrayReverse( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} ' ----- Show the elements before reversal. result.AppendLine("Before reversing:") For Each fruit As String In arrayReverse result.AppendLine(fruit) Next fruit ' ----- Show the elements after reversal. result.AppendLine( ) result.AppendLine("After reversing:") Array.Reverse(arrayReverse) For Each fruit As String In arrayReverse result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
The StringBuilder
fills first
with the strings from the original array, then with the reversed
array’s contents for comparison. Figure 8-3 shows the results
as displayed by the StringBuilder
in the message box.
Recipe 8.2 shows another method of arranging the elements of an array.
You need to insert a new value at an arbitrary location in the middle of an array.
Sample code folder: Chapter 08ArrayInsertion
Unlike some of the collection classes in .NET, arrays do not include a method that lets you insert an element in the middle of an array. Instead, you have to create a new array and copy the elements of the original array into it, reserving space for the new element. The code in this recipe implements such a method.
Because arrays can be created using any data type, we will require a generic method capable of handling any data:
Public Sub InsertArrayElement(Of T) ( _ ByRef sourceArray( ) As T, _ ByVal insertIndex As Integer, _ ByVal newValue As T) ' ----- Insert a value in the middle of an array. Dim newPosition As Integer Dim counter As Integer ' ----- Get a valid positon, checking for boundaries. newPosition = insertIndex If (newPosition < 0) Then newPosition = 0 If (newPosition > sourceArray.Length) Then _ newPosition = sourceArray.Length ' ----- Make room in the array. Array.Resize(sourceArray, sourceArray.Length + 1) ' ----- Move the after-index items. For counter = sourceArray.Length - 2 To newPosition Step -1 sourceArray(counter + 1) = sourceArray(counter) Next counter ' ----- Store the new element. sourceArray(newPosition) = newValue End Sub
The code stretches the initial array, making it one position larger. It then shifts some of the elements one position higher to make room for the new element. Finally, it saves the new element at the desired position.
To use this method, pass it an array of any type, and also indicate the type used for the generic parameter.
You can insert the new value at position 0, just before the very first element, or at a position one greater than the maximum current index of the array. Insert positions outside this range adjust themselves to fit the valid range.
The following example demonstrates calling the InsertArrayElement()
method by first
creating a string array of fruit names and then inserting an element
in the middle:
Dim result As New System.Text.StringBuilder Dim arrayInsert( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} ' ----- Show the contents before insertion. result.AppendLine("Before insertion:") For Each fruit As String In arrayInsert result.AppendLine(fruit) Next fruit ' ----- Insert more fruit. InsertArrayElement(Of String)(arrayInsert, 2, "Lemons") ' ----- Show the contents after insertion. result.AppendLine( ) result.AppendLine("After insertion:") For Each fruit As String In arrayInsert result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
The string “Lemons” is inserted at position 2 (counting from zero) in the array. The results are shown in Figure 8-4.
Recipe 8.7 also discusses adding elements to an array.
You want to randomize the order of the elements in an array efficiently.
Sample code folder: Chapter 08ShuffleArray
Write a routine that randomly rearranges the elements of an array. The code in this recipe does this using an array of any data type.
The Shuffle()
method
presented here swaps each element of the array with a randomly
selected element from elsewhere in the array. Sometimes this may cause
an element to be swapped with itself, but that doesn’t make the
results any less random. By sequencing through all elements, the
algorithm guarantees that each one will be swapped at least
once:
Public Sub Shuffle(ByRef shuffleArray( ) As Object) ' ----- Reorder the elements of an array in a random order. Dim counter As Integer Dim newPosition As Integer Dim shuffleMethod As New Random Dim tempObject As Object For counter = 0 To shuffleArray.Length - 1 ' ----- Determine the new position. newPosition = shuffleMethod.Next(0, _ shuffleArray.Length - 1) ' ----- Reverse two elements. tempObject = shuffleArray(counter) shuffleArray(counter) = shuffleArray(newPosition) shuffleArray(newPosition) = tempObject Next counter End Sub
The following code creates a string array of fruit names, shuffles the array, and displays the array contents both before and after the shuffling:
Dim result As New System.Text.StringBuilder Dim arrayShuffle( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} ' ----- Show the pre-random results. result.AppendLine("Before shuffling:") For Each fruit As String In arrayShuffle result.AppendLine(fruit) Next fruit ' ----- Randomize. Shuffle(arrayShuffle) ' ----- Show the post-random results. result.AppendLine( ) result.AppendLine("After shuffling:") For Each fruit As String In arrayShuffle result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
Figure 8-5 shows the results from running the sample code, listing the array’s contents before and after the shuffling. Your output may vary due to the random nature of the test.
Recipe 8.6 uses a portion of this recipe’s code to generically reverse two array elements.
You want to swap the contents of any two elements in an array.
Sample code folder: Chapter 08SwapArrayElements
Write a custom method that reverses the positions of two
specific array elements. The code in this recipe implements a Swap()
method that does just that.
The Swap()
method accepts an
array of any data type, plus the positions of two elements to swap.
After doing some boundary checking, it reverses the elements:
Public Sub Swap(ByRef swapArray( ) As Object, _ ByVal first As Integer, ByVal second As Integer) ' ----- Reverse two elements of an array. Dim tempObject As Object ' ----- Check for invalid positions. If (first < 0) Then Return If (first >= swapArray.Length) Then Return If (second < 0) Then Return If (second >= swapArray.Length) Then Return If (first = second) Then Return ' ----- Reverse two elements. tempObject = swapArray(first) swapArray(first) = swapArray(second) swapArray(second) = tempObject End Sub
Several lines of this code simply check to make sure the indexes into the array are valid. If they are out of range, no swapping takes place.
The following code demonstrates the Swap()
method by creating a string array of
fruit names and swapping the contents at the first and third indexes
into the array. The ArrayHelper
is
instanced to accept string parameters, and the string array is passed
to its Swap()
method:
Dim result As New System.Text.StringBuilder Dim arraySwap( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} ' ----- Show the pre-swap data. result.AppendLine("Before swap:") For Each fruit As String In arraySwap result.AppendLine(fruit) Next fruit ' ----- Swap two elements. Swap(arraySwap, 1, 3) ' ----- Show the post-swap data. result.AppendLine( ) result.AppendLine("After swap:") For Each fruit As String In arraySwap result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
Figure 8-6 shows the array’s contents before and after elements 1 and 3 are swapped. Notice that the array elements start at zero, so the swap is between the second and fourth values in the array.
Recipe 8.5 shows how to randomly rearrange the contents of an entire array.
You want to add an unknown number of elements to an array, resizing the array as needed, but you don’t want to lose any data in the process.
Sample code folder: Chapter 08SwapArrayElements
Visual Basic 2005 provides the
ReDim Preserve
command to resize an array
without losing any of the array’s current contents.
Actually, you can lose some contents of an array using ReDim Preserve
, but only if you are
decreasing the array’s size. ReDim
Preserve
is most often used to grow an array, and it is
ideal for adding new elements on the fly, without losing any data
already in the array.
For example, the following code creates an integer array and then loops to grow it one element at a time. A number is stored in each new array element as the array grows:
Dim result As New System.Text.StringBuilder Dim growingArray( ) As String = Nothing ' ----- Add elements to the array. For counter As Integer = 0 To 2 ReDim Preserve growingArray(counter) growingArray(counter) = (counter + 1).ToString Next counter ' ----- Display the results. For Each workText As String In growingArray result.AppendLine(workText) Next workText MsgBox(result.ToString( ))
Figure 8-7 displays the simple integer array that was resized, one element at a time, to hold the three numbers shown.
One nice thing about ReDim
Preserve
is that it works with arrays that are empty or set
to Nothing
, as shown in the sample
code.
The Array
object’s Resize()
method provides similar
functionality.
Recipe 8.4 shows how to insert elements into the middle of an existing array, instead of just at the end.
You want to copy elements of one array into another without having to move the items one at a time.
Sample code folder: Chapter 08 CopyingArrays
Use the Array.Copy()
method
to copy a sequential subset of one array to another array of the same
type. Or, if the entire array is to be copied, use the array’s
Clone()
method. Assign one array
directly to another only if you want both variables to reference the
same contents in memory.
This recipe explores several ways to copy elements from one array to another, and one way that appears to do a copy but doesn’t. It’s important to know the difference between these various techniques. The following block of code demonstrates all of them and displays the results in a message box:
Dim result As New System.Text.StringBuilder Dim arrayA( ) As String = _ {"One", "Two", "Three", "Four", "Five", "Six"} result.Append("arrayA: ").AppendLine(Join(arrayA, ",")) Dim arrayB( ) As String = _ {"A", "B", "C", "D", "E", "E", "F", "G", "H"} result.AppendLine( ) result.Append("arrayB: ").AppendLine(Join(arrayB, ",")) ' ----- Make a reference copy. Dim arrayC( ) As String = arrayA result.AppendLine( ) result.AppendLine("Dim arrayC( ) As String = arrayA") result.Append("arrayC: ").AppendLine(Join(arrayC, ",")) arrayC(4) = "Was a five here" result.AppendLine( ) result.AppendLine("arrayC(4) = ""Was a five here""") result.Append("arrayA: ").AppendLine(Join(arrayA, ",")) ' ----- Make a full, unique copy of all elements. Dim arrayD( ) As String = arrayA.Clone result.AppendLine( ) result.AppendLine("Dim arrayD( ) As String = arrayA.Clone") result.Append("arrayD: ").AppendLine(Join(arrayD, ",")) ' ----- Copy elements by position. Array.Copy(arrayB, 0, arrayD, 1, 3) result.AppendLine( ) result.AppendLine("Array.Copy(arrayB, 0, arrayD, 1, 3)") result.Append("arrayD: ").AppendLine(Join(arrayD, ",")) MsgBox(result.ToString( ))
Let’s break down this code into smaller chunks so we can take a
closer look. The first three sections create two string arrays,
arrayA
and arrayB
, containing simple strings so we can
follow the action later. The first line of the next section is where
it gets interesting:
Dim arrayC( ) As String = arrayA
This appears to be an array copy command, but it isn’t. The two array names both reference the same contents in memory. In other words, the reference to the array is copied, not the array itself. The code in the next section demonstrates this clearly:
arrayC(4) = "Was a five here" result.AppendLine( ) result.AppendLine("arrayC(4) = ""Was a five here""") result.Append("arrayA: ").AppendLine(Join(arrayA, ","))
The new string is assigned to arrayC(4)
, but when the contents of arrayA
are formatted for display the new
string appears there, too. As Figure 8-8 shows, the new
string appears as an element of both arrayA
and arrayC
.
The next-to-last code section demonstrates the proper way to
truly copy an entire array to another. The array’s Clone()
method returns a clone, or identical
duplicate, of the original array. The result is that the array’s
contents are copied to a new place in memory. In the example code, the
reference to the cloned copy of the array is assigned to arrayD
:
Dim arrayD( ) As String = arrayA.Clone result.AppendLine( ) result.AppendLine("Dim arrayD( ) As String = arrayA.Clone") result.Append("arrayD: ").AppendLine(Join(arrayD, ","))
Finally, the last code section demonstrates the use of the
Array
class’s Copy()
method to copy part of one array to another. In this case both arrays
must exist before the copy, and the indexes must point to real
locations within the arrays. There are several overloaded versions of
the Copy()
method. The version
shown here lets you move array elements starting at a given indexed
position to any position in the destination array, and the number of
elements to copy limits how much data is copied:
Array.Copy(arrayB, 0, arrayD, 1, 3) result.AppendLine( ) result.AppendLine("Array.Copy(arrayB, 0, arrayD, 1, 3)") result.Append("arrayD: ").AppendLine( Join(arrayD, ","))
arrayB's
contents, starting
at index 0, are copied into arrayD
,
starting at index 1, and three items are copied. If you’ve followed
along carefully as these sections of code manipulate the contents of
the arrays, you’ll see that the result shown in Figure 8-8 does verify this
copy action.
You need to write data stored in an array to a comma-separated-values ( CSV) file. This is often done to provide input to Excel.
Sample code folder: Chapter 08CreateCSVFiles
Use the String
class’s
Join()
method to concatenate array
contents into strings, using a comma as the character to insert at the
join points. Then write the string or strings to a file using the
WriteAllText()
method provided by the
My.Computer.FileSystem
object.
In many cases you’ll have several data items that you want to
appear in each of several rows of a spreadsheet. This is accomplished
by separating each data item in each row with a comma, and separating
the rows from each other using newline characters. The following code
demonstrates various ways to accomplish this. headings
is a string array containing three
words. The Join()
method
concatenates this array into a single string with commas separating
each word. To simplify the example, several more similar
comma-separated strings are concatenated to the string, each separated
with vbNewLine
characters. The
resulting string is written to a file named Test.csv in a single command using the
My.Computer.FileSystem.WriteAllText()
method:
Dim result As New System.Text.StringBuilder Dim headings( ) As String = {"Alpha", "Beta", "Gamma"} Dim workText As String = String.Join(",", headings) ' ----- Prepare the raw data. workText &= vbNewLine workText &= "1.1, 2.3, 4.5" & vbNewLine workText &= "4.2, 7.9, 3.1" & vbNewLine workText &= "3.5, 2.2, 9.8" & vbNewLine ' ----- Convert it to CSV and save it to a file. Dim filePath As String = _ My.Computer.FileSystem.CurrentDirectory & "Test.csv" My.Computer.FileSystem.WriteAllText(filePath, workText, False) result.Append("File written: ") result.AppendLine(filePath) result.AppendLine( ) result.AppendLine("File contents:") result.Append(workText) MsgBox(result.ToString( ))
The remaining lines of example code display the new Test.csv file contents, as shown in Figure 8-9.
Recipe 8.10 is the reverse of this recipe.
You need to read a CSV file into an array.
Sample code folder: Chapter 08ReadCSVFiles
Use the Split()
function to
parse the file’s content to fill an array.
Today’s computers generally have a lot of memory, which often
allows entire files to be read into a single string in one operation.
If you have an extremely large CSV file, you might want to read the
file one line at a time. In either case, the
Split()
function provides a great tool for
parsing the comma-separated values so they can be copied into an
array.
The following code reads the entire file created in the previous
recipe into a single string, and then splits this string into an array
of strings, lineData
, using the
newline characters as the split point. Each line is then further split
at the comma character separating individual words. If the CSV file
contains numbers, this is the point where each “word” of the text from
the file could be converted to Double,
Integer
, or whatever type is appropriate. In this example,
however, the words are simply reformatted for display and verification
in a message box:
Dim result As New System.Text.StringBuilder Dim wholeFile As String Dim lineData( ) As String Dim fieldData( ) As String ' ----- Read in the file. Dim filePath As String = _ My.Computer.FileSystem.CurrentDirectory & "Test.csv" wholeFile = My.Computer.FileSystem.ReadAllText(filePath) ' ----- Process each line. lineData = Split(wholeFile, vbNewLine) 'OR: lineData = wholeFile.Split(New String( ) {vbNewLine}, _ ' StringSplitOptions.None) For Each lineOfText As String In lineData ' ----- Process each field. fieldData = lineOfText.Split(",") For Each wordOfText As String In fieldData result.Append(wordOfText) result.Append(Space(1)) Next wordOfText result.AppendLine( ) Next lineOfText MsgBox(result.ToString( ))
String
objects have a
Split()
method, and Visual Basic
2005 also provides a Split()
function. Notice the commented-out line in the previous code. This
line demonstrates how workText
can
be split using the string’s Split()
method instead of using the Split()
function, and it’s useful to compare that line with the line just
above it. In both cases linedata
is
filled with the lines of the file, but the syntax is different for
these two Split()
variations. With
the string Split()
method, only
individual characters or an array of strings can be designated for the
split point. In other words, you’ll run into trouble if you try to
split the lines in the following way:
lineData = workText.Split(vbNewLine, StringSplitOptions.None)
The special constant vbNewLine
is actually two characters in
length (carriage return and line feed), and the resulting strings will
all still contain one of these two characters. It took considerable
time and effort to debug the rather strange results when we first
encountered this problem. To avoid it, pass an array of multicharacter
strings to the string Split()
method, as shown in the commented-out line in the code above, or use
the Visual Basic 2005 Split()
function, which has a simpler syntax and does accept multicharacter
strings for the split point. Figure 8-10 shows the result
of running the example code.
Recipe 8.9 shows the reverse of this recipe.
Recipe 8.12
discusses the differences between the Split()
function and the Split()
method in more detail. Also, see
Recipe 5.44 for more
on the Split()
function and
method.
You want to store data in a two-dimensional array, but the number of items in each row varies. You don’t want to dimension the array for the longest row and waste a lot of space in the array.
Sample code folder: Chapter 08MultivalueArray
Instead of creating a two-dimensional array, create an array of arrays, sometimes referred to as a multivalue array.
A two-dimensional array is identified by its single pair of parentheses containing one comma separating the two indexes. A multivalue array has two sets of parentheses, and the contents are stored as one-dimensional arrays stored in the elements of another one-dimensional array. The following code demonstrates a multivalue array containing three string arrays of varying lengths:
Dim result As New System.Text.StringBuilder Dim multiValue(2)( ) As String Dim counter1 As Integer Dim counter2 As Integer ' ----- Build the multivalue array. multiValue(0) = New String( ) {"alpha", "beta", "gamma"} multiValue(1) = New String( ) _ {"A", "B", "C", "D", "E", "F", "G", "H"} multiValue(2) = New String( ) {"Yes", "No"} ' ----- Format the array for display. For counter1 = 0 To multiValue.Length - 1 For counter2 = 0 To multiValue(counter1).Length - 1 result.Append(multiValue(counter1)(counter2)) result.Append(Space(1)) Next counter2 result.AppendLine( ) Next counter1 MsgBox(result.ToString( ))
Inside the nested For
loops
is a line where each string from the array of arrays is accessed to
form the results displayed in Figure 8-11. Two pairs of
parentheses are used to index the specific string stored in the
multivalue array:
multiValue(counter1)(counter2)
A true two-dimensional array element would be accessed with a pair of indexes within one set of parentheses, as in the following:
twoDimArray(counter1, counter2)
You have a string that contains data delimited by one or more characters, and you want to divide the parts into an array. Or you want to reverse the process, moving array elements into a delimited string.
Sample code folder: Chapter 08SplitAndJoin
The Split()
and
Join()
functions provided as part of the
Visual Basic 2005 language, and the similar Split()
and Join()
methods of the string data type,
provide a flexible and powerful way to manipulate string
arrays.
The Split()
and Join()
functions and methods are described
in Chapter 5, which deals with
strings, but here they are presented in the context of how they add
useful functionality when working with string arrays.
Split()
operates on a single
string and returns a string array comprised of pieces of the original
string split apart at the designated points. You can split the string
at all occurrences of a given single character, at any occurrence of
any single character in an array of characters, at any occurrence of
any multicharacter string in a string array, or at any occurrence of a
single multicharacter string. The overloaded versions of these methods
provide considerable flexibility.
You do need to be careful when splitting a string at all
occurrences of a single multi-character string. The string Split()
method accepts a single string as
the split parameter, but it uses only the first character of the
string to define where to do the split. To use any multicharacter
string for the split point, you must pass an array of strings instead
of a single string. The string array can have just one string in it,
but it must be an array in order to work as expected. (The Visual
Basic 2005 Split()
function doesn’t
have this limitation.)
To illustrate this, the following code splits a string at all
occurrences of “en” and joins it again using Join()
. The string to insert at the join
points is “EN”. This effectively uppercases all occurrences of “en” in
the string. The string array splitArray()
is the string array created by
the split:
Dim workText As String workText = _ "This sentence will have all ""en"" characters uppercased." Dim splitArray( ) As String = {"en"} Dim workArray( ) As String = _ workText.Split(splitArray, StringSplitOptions.None) workText = String.Join("EN", workArray) MsgBox(workText)
Figure 8-12 shows the result.
See Recipe 5.18
and Recipe 5.44 for
more on the Split()
and Join()
functions and methods. Recipe 5.16 gives an example
of using the Replace()
function to
replace all occurrences of a given substring.
You want to format the contents of an array into a string, but
the ToString()
method returns only a string
description of the array reference.
Sample code folder: Chapter 08 PrintArrays
Build generic helper routines that format the contents of an array nicely.
The ToString()
method all
objects inherit from System.Object
is ideal in most cases; it gives you a quick and simple string
representation of any object’s contents. However with arrays, the
ToString()
method returns a
description of the array, rather than a listing of its contents. This
makes sense in that the array variable name contains a reference, not
data, but it makes it tricky to get a listing of the array’s
contents.
The following code demonstrates how to format the contents of
both one-and two-dimensional arrays generically. The two ToBracedString()
functions accept an
appropriately sized array and return a string with braces surrounding
the array elements. The braces, data items, and separating commas are
formatted in the same way as required when initializing an array in
code. For example, output from this function for a two-dimensional
array will have nested braces to indicate the layout of the array’s
rows and columns.
Here are the ToBracedString()
functions used to display one-and two-dimensional arrays:
Public Function ToBracedString(Of T)(ByVal sourceArray( ) _ As T) As String ' ----- Display the contents of a one-dimensional array. Dim result As New System.Text.StringBuilder Dim counter As Integer result.Append("{") For counter = 0 To sourceArray.Length - 1 result.Append(sourceArray(counter).ToString( )) If (counter < (sourceArray.Length - 1)) Then _ result.Append(",") Next counter result.Append("}") Return result.ToString( ) End Function Public Function ToBracedString(Of T)(ByVal sourceArray(,) _ As T) As String ' ----- Display the contents of a two-dimensional array. Dim result As New System.Text.StringBuilder Dim counter1 As Integer Dim counter2 As Integer Dim rank1Size As Integer = sourceArray.GetLength(0) Dim rank2Size As Integer = sourceArray.GetLength(1) result.Append("{") For counter1 = 0 To sourceArray.GetLength(0) - 1 result.Append("{") For counter2 = 0 To rank2Size - 1 result.Append(sourceArray(counter1, _ counter2).ToString( )) If (counter2 < (rank2Size - 1)) Then _ result.Append(",") Next counter2 result.Append("}") If (counter1 < (rank1Size - 1)) Then result.Append(",") Next counter1 result.Append("}") Return result.ToString( ) End Function
In the following code, two arrays are created and initialized with sample data,
and their contents, as returned by ToBracedString()
, are displayed for
review:
Dim result As New System.Text.StringBuilder Dim arrayA( ) As Integer = {1, 2, 3} Dim arrayB(,) As Integer = {{1, 2, 3}, {4, 5, 6}} ' ----- Show the typical ToString results. result.AppendLine("arrayA.ToString… ") result.AppendLine(arrayA.ToString) result.AppendLine( ) ' ----- Format arrayA nicely. result.AppendLine("ToBracedString(arrayA)… ") result.AppendLine(ToBracedString(Of Integer)(arrayA)) result.AppendLine( ) ' ----- Format arrayB nicely. result.AppendLine("ToBracedString(arrayB)… ") result.Append(ToBracedString(Of Integer)(arrayB)) MsgBox(result.ToString( ))
Compare the braced initialization strings in the first few lines of the previous code with the output as shown in Figure 8-13. The goal was to duplicate the same simple format.
Recipe 8.1 shows how to properly format new array content in code.
You want to process all the elements of an array without the overhead of creating extra variables, and you’d like to minimize the scope of all working variables.
Sample code folder: Chapter 08ForEachLoops
Use the For Each
looping
construct to process each element of an array.
The following code creates a simple string array of fruit names,
then processes each string in the array inside a For Each
loop:
Dim result As New System.Text.StringBuilder Dim fruitArray( ) As String = { _ "Oranges", "Apples", "Grapes", "Bananas", "Blueberries"} For Each fruit As String In fruitArray result.AppendLine(fruit) Next fruit MsgBox(result.ToString( ))
The For Each
line declares a
temporary variable named fruit
that
exists only for the duration of the For
Each
loop. This ties the variable name closely to the
processing going on locally and frees up resources as soon as that
processing is completed. Also, there is no need to access the length
of the array to control the looping because the loop implicitly
processes all elements, no matter what the array’s size is. (The
standard For
loop syntax requires a
separate counting variable and access to the array’s length.) Figure 8-14 shows the results
displayed by the example code.
You want to pass and return arrays to and from methods as easily as other simple variable types.
Sample code folder: Chapter 08ArrayParameters
Unlike Visual Basic 6.0, in Visual Basic 2005 it’s easy to pass and return any type of object, including arrays.
The following code provides a fun example by passing a string
array to a function that returns an even bigger string array. The
names of the four card suits are placed in a small string array. This
array is passed to FillDeckOfCards()
, which creates and returns
a string array containing the names of all the cards in a deck:
Dim result As New System.Text.StringBuilder Dim suits( ) As String = {"Spades", "Hearts", "Diamonds", "Clubs"} Dim cardDeck( ) As String = FillDeckOfCards(suits) Shuffle(cardDeck) For counter As Integer = 0 To 6 result.AppendLine(cardDeck(counter)) Next counter MsgBox(result.ToString( ))
The Shuffle()
method
(designed in Recipe
8.5) shuffles the returned array, and the first seven cards in
the array are displayed for review, as shown in Figure 8-15. Of course, your
results will vary based on the state of your random number
generator.
The FillDeckOfCards( )
function is passed a string array and returns one, too:
Public Function FillDeckOfCards(ByVal suit As String()) As String( ) Dim deck(51) As String Dim cardNumber As Integer Dim suitNumber As Integer For counter As Integer = 0 To 51 cardNumber = counter Mod 13 suitNumber = counter 13 Select Case cardNumber Case 0 deck(counter) = "Ace of " Case 10 deck(counter) = "Jack of " Case 11 deck(counter) = "Queen of " Case 12 deck(counter) = "King of " Case Else deck(counter) = cardNumber.ToString & " of " End Select deck(counter) &= suit(suitNumber) Next counter Return deck End Function
You may pass and return objects in Visual Basic 2005, a process
similar to using Variants
in Visual
Basic 6.0. But in general, it is better to pass and return explicitly
typed arrays, as in the example presented here. This prevents the
runtime overhead required for constantly converting variable types,
and it helps the compiler determine at compile time if you’re
attempting to pass incompatible data. In general, consider overloaded
methods and generics as two ways to enhance the
flexibility of methods, while optimizing the compile- and runtime
operations.
Recipe 8.16 discusses similar functionality.
You want to return an array from a function.
Sample code folder: Chapter 08FunctionArrays
Declare the function to return an array of the desired type and
do so in the function’s Return
statement.
This recipe is very similar to Recipe 8.15, but the lesson is worth repeating: arrays of any type and size are easily passed to and returned from methods. The following example demonstrates a function that returns an array of 16 hexadecimal characters. The array is joined into a string and displayed for review in a message box, as shown in Figure 8-16:
Dim result As New System.Text.StringBuilder result.Append("Hexadecimal characters: ") result.Append(String.Join(",", HexadecimalCharacters( ))) MsgBox(result.ToString( ))
The HexadecimalCharacters( )
function includes a set of parentheses at the very end of the function
declaration. This indicates that the function will return a string
array and not just an ordinary string. The Return
statement near the end of the
function returns the string array hexChars(
)
:
Public Function HexadecimalCharacters() As String( ) ' ----- Return the first 16 hex numbers as an array. Dim hexChars(15) As String For counter As Integer = 0 To 15 hexChars(counter) = Hex(counter) Next counter Return hexChars End Function
Recipe 8.15 discusses similar functionality.
You want a simple example of a collection to demonstrate the basics of using the collection object.
Sample code folder: Chapter 08 Collections
This recipe provides a simple example collection to use as a starting point for further explorations of the topic.
Collections provide capabilities similar to those of arrays, but they have some advantages. A collection is inherently more dynamic and allows the insertion and deletion of items, and it can be resized without loss of any current contents. You can do these same tasks with arrays, but collections make the whole process much simpler and more straightforward.
The following example creates a collection of strings. Each
string(in this case they are all just simple words) is added to the
collection using the collection’s Add()
method. After all words are added to
the collection, its entire contents are retrieved for display and
review, as shown in Figure
8-17:
Dim result As New System.Text.StringBuilder Dim wordCollection As New Collection ' ----- Build the collection. wordCollection.Add("This") wordCollection.Add("is") wordCollection.Add("a") wordCollection.Add("collection") wordCollection.Add("of") wordCollection.Add("words") ' ----- Display the collection. For Each word As String In wordCollection result.Append(word) result.Append(Space(1)) Next word MsgBox(result.ToString( ))
As with arrays, you can retrieve each item from the collection
using an index, or you can use the For
Each
loop, as shown in this example. Unlike with arrays,
however, you can optionally pass a key string to the Add()
method to provide a way to retrieve
items from a collection based on their keys.
You can store varying types of data in the same collection. This provides some flexibility, but in most cases you should store only the same type of data in any single collection. Methods you write to process the collection’s data will need to handle whatever data type is stored in the collection, so keeping it consistent greatly simplifies the coding requirements.
If data-type issues become a problem with your collections, consider using the new generic collections instead.
Recipes 8.18, 8.19, through 8.20 show other features of collections.
You want to insert a new item in the middle of a collection, rather than just adding it to the end of the collection.
Sample code folder: Chapter 08Collections
Use the Add()
method, but include its optional
parameters to control the insertion point.
The Add()
method by default
appends items to the end of a collection, but optional
parameters can modify this behavior. Here’s the general syntax of the
Add()
method:
variable
.Add(content, key, before, after
)
All parameters other than content
are
optional, and you can’t supply values for both
before
and after
in the same statement. before
and
after
represent the element positions
before or after which the new item should be inserted. In the next
code example, the word “slightly” is inserted after position 3 because
the after
parameter passed to the Add()
method is a 3. The word “longer” is
then inserted into the collection before the fifth position, because
the before
parameter of the Add()
method is a 5:
Dim result As New System.Text.StringBuilder Dim wordCollection As New Collection ' ----- Start with a basic collection. wordCollection.Add("This") wordCollection.Add("is") wordCollection.Add("a") wordCollection.Add("collection") wordCollection.Add("of") wordCollection.Add("words") ' ----- Insert a word after item 3. wordCollection.Add("slightly", , , 3) ' ----- Insert a word before item 5. wordCollection.Add("longer", , 5) ' ----- Display the collection. For Each word As String In wordCollection result.Append(word) result.Append(Space(1)) Next word MsgBox(result.ToString( ))
The results of these two “before and after” additions into the collection are shown in Figure 8-18.
Recipes 8.17, 8.19, and 8.20 show other features of collections.
You need to delete an item from a collection.
Sample code folder: Chapter 08Collections
Use the collection’s Remove()
method, passing either the position
of the item or its key string.
The following example fills a collection with several words using “key strings,” identifiers that provide an optional way to specify each item. The item at index position 5 is then removed, followed by the item with key “six”:
Dim result As New System.Text.StringBuilder Dim wordCollection As New Collection ' ----- Start with a basic collection. wordCollection.Add("This", "one") wordCollection.Add("is", "two") wordCollection.Add("a", "three") wordCollection.Add("collection", "four") wordCollection.Add("of", "five") wordCollection.Add("words", "six") ' ----- Remove an element by position. wordCollection.Remove(5) ' ----- Remove an element by key. wordCollection.Remove("six") ' ----- Dipslay the collection. For Each word As String In wordCollection result.Append(word) result.Append(Space(1)) Next word MsgBox(result.ToString( ))
Once item number 5 is removed, the item at position 6 moves to position 5. This means that removing items 5 and 6 both by number wouldn’t work; you would need to remove the item at position 5 twice in a row. This hints at the usefulness of using key strings to uniquely identify each item, especially when items might be freely added to or removed from the collection over time. Figure 8-19 shows the contents of the collection after the two items are removed.
Recipes 8.17, 8.18, and 8.20 show other features of collections.
You want to process all the items in a collection one at a time.
Sample code folder: Chapter 08Collections
Use a For Each
loop, or use the collection’s
Count property in a For…Next
loop.
The For Each
loop is the
recommended way to process items in a collection because you don’t
need an index variable, you don’t have to access the Count
property of the collection, and each
item in the collection is automatically retrieved (i.e., you don’t
have to explicitly access each indexed item).
The following code shows both a For…Next
loop and a For Each
loop used to access the same
collection. Each loop creates a single line of the output display,
showing the contents of each item in the collection:
Dim result As New System.Text.StringBuilder Dim numberCollection As New Collection ' ----- Start with a basic collection. numberCollection.Add(14, "C") numberCollection.Add(25, "D") numberCollection.Add(36, "E") numberCollection.Add(47, "A") numberCollection.Add(58, "B") ' ----- Scan the collection with a loop counter. ' Collections are base-1, not base-0. For counter As Integer = 1 To numberCollection.Count result.Append(numberCollection(counter)) result.Append(",") Next counter ' ----- Remove the ending comma. result.Length -= 1 result.AppendLine( ) ' ----- Scan the collection by item. For Each number As Integer In numberCollection result.Append(number) result.Append(",") Next number ' ----- Remove the ending comma. result.Length -= 1 result.AppendLine( ) ' ----- Retrieve items by key. result.Append(numberCollection("A")).Append(",") result.Append(numberCollection("B")).Append(",") result.Append(numberCollection("C")).Append(",") result.Append(numberCollection("D")).Append(",") result.Append(numberCollection("E")) ' ----- Display the results. MsgBox(result.ToString( ))
The third line of the output is the same collection accessed in the order of the item keys, instead of the default order, which is based on the item positions in the collection. Figure 8-20 shows the collection’s items as accessed in each of these three ways.
Recipes 8.17, 8.18, through 8.19 show other features of collections.
3.22.216.254