Chapter 8. Arrays and Collections

Introduction

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.

8.1. Filling an Array While Declaring It

Problem

You want to fill an array with starting values without having to explicitly assign each array element individually.

Solution

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.

Discussion

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

8.2. Sorting Array Elements

Problem

You want to sort the elements of an array.

Solution

Sample code folder: Chapter 08SortingArrays

Use the Sort() method of the Array class.

Discussion

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 arrays using the shared Sort( ) method of the Array class
Figure 8-1. Sorting arrays using the shared Sort( ) method of the Array class

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.

Sorting custom data using the IComparable interface
Figure 8-2. Sorting custom data using the IComparable interface

See Also

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.

8.3. Reversing an Array

Problem

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.

Solution

Sample code folder: Chapter 08ArrayReversal

The Array class provides a shared Reverse() method that reverses the order of its elements.

Discussion

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.

Reversing the contents of an array with the shared Array.Reverse( ) method
Figure 8-3. Reversing the contents of an array with the shared Array.Reverse( ) method

See Also

Recipe 8.2 shows another method of arranging the elements of an array.

8.4. Inserting into an Array

Problem

You need to insert a new value at an arbitrary location in the middle of an array.

Solution

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.

Discussion

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.

Inserting values into an array
Figure 8-4. Inserting values into an array

See Also

Recipe 8.7 also discusses adding elements to an array.

8.5. Shuffling an Array

Problem

You want to randomize the order of the elements in an array efficiently.

Solution

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.

Discussion

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.

Randomizing an array’s elements with the Shuffle( ) method
Figure 8-5. Randomizing an array’s elements with the Shuffle( ) method

See Also

Recipe 8.6 uses a portion of this recipe’s code to generically reverse two array elements.

8.6. Swapping Two Array Values

Problem

You want to swap the contents of any two elements in an array.

Solution

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.

Discussion

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.

Swapping two array elements with the Swap( ) method
Figure 8-6. Swapping two array elements with the Swap( ) method

See Also

Recipe 8.5 shows how to randomly rearrange the contents of an entire array.

8.7. Resizing Arrays Without Losing Existing Values

Problem

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.

Solution

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.

Discussion

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.

Resizing an array on the fly with ReDim Preserve
Figure 8-7. Resizing an array on the fly with ReDim Preserve

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.

See Also

Recipe 8.4 shows how to insert elements into the middle of an existing array, instead of just at the end.

8.8. Quickly Copying Part of an Array into Another

Problem

You want to copy elements of one array into another without having to move the items one at a time.

Solution

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.

Discussion

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.

Various ways to copy data between arrays
Figure 8-8. Various ways to copy data between arrays

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.

8.9. Writing a Comma-Separated-Values File from a String Array

Problem

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.

Solution

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.

Discussion

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.

Writing CSV files from array data
Figure 8-9. Writing CSV files from array data

See Also

Recipe 8.10 is the reverse of this recipe.

8.10. Reading a Comma-Separated-Values File into a String Array

Problem

You need to read a CSV file into an array.

Solution

Sample code folder: Chapter 08ReadCSVFiles

Use the Split() function to parse the file’s content to fill an array.

Discussion

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.

Parsing CSV files into arrays using Split( )
Figure 8-10. Parsing CSV files into arrays using Split( )

See Also

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.

8.11. Using a Multivalue Array Instead of a Two-Dimensional Array

Problem

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.

Solution

Sample code folder: Chapter 08MultivalueArray

Instead of creating a two-dimensional array, create an array of arrays, sometimes referred to as a multivalue array.

Discussion

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)
Using multivalue arrays to store a variable number of items in each row of a two-dimensional array
Figure 8-11. Using multivalue arrays to store a variable number of items in each row of a two-dimensional array

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)

8.12. Converting Between Delimited Strings and Arrays

Problem

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.

Solution

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.

Discussion

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.

Using Split() and Join( ) to replace all occurrences of a substring
Figure 8-12. Using Split() and Join( ) to replace all occurrences of a substring

Tip

There is a better way to replace all occurrences of a substring with another one: use the Replace( ) function. The following line of code has the same result as the previous code:

	workText = Replace(workText, "en", "EN")

See Also

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.

8.13. Formatting an Array as a Single String

Problem

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.

Solution

Sample code folder: Chapter 08 PrintArrays

Build generic helper routines that format the contents of an array nicely.

Discussion

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.

Using the ToBracedString() functions to format the contents of an array
Figure 8-13. Using the ToBracedString() functions to format the contents of an array

See Also

Recipe 8.1 shows how to properly format new array content in code.

8.14. Iterating Through Array Elements

Problem

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.

Solution

Sample code folder: Chapter 08ForEachLoops

Use the For Each looping construct to process each element of an array.

Discussion

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.

Processing arrays with For Each loops
Figure 8-14. Processing arrays with For Each loops

8.15. Passing Arrays to Methods

Problem

You want to pass and return arrays to and from methods as easily as other simple variable types.

Solution

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.

Discussion

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.

Passing and returning arrays
Figure 8-15. Passing and returning arrays

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.

See Also

Recipe 8.16 discusses similar functionality.

8.16. Returning Arrays from Functions

Problem

You want to return an array from a function.

Solution

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.

Discussion

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( ))
Returning an array of hexadecimal characters from a function
Figure 8-16. Returning an array of hexadecimal characters from a function

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

See Also

Recipe 8.15 discusses similar functionality.

8.17. Creating a Collection

Problem

You want a simple example of a collection to demonstrate the basics of using the collection object.

Solution

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.

Discussion

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( ))
A collection of strings
Figure 8-17. A collection of strings

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.

See Also

Recipes 8.18, 8.19, through 8.20 show other features of collections.

8.18. Inserting an Item into a Collection

Problem

You want to insert a new item in the middle of a collection, rather than just adding it to the end of the collection.

Solution

Sample code folder: Chapter 08Collections

Use the Add() method, but include its optional parameters to control the insertion point.

Discussion

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.

Using a collection’s Add( ) method to insert items at a given point
Figure 8-18. Using a collection’s Add( ) method to insert items at a given point

See Also

Recipes 8.17, 8.19, and 8.20 show other features of collections.

8.19. Deleting a Collection Item

Problem

You need to delete an item from a collection.

Solution

Sample code folder: Chapter 08Collections

Use the collection’s Remove() method, passing either the position of the item or its key string.

Discussion

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.

The Remove( ) method removes items from a collection by position or by key
Figure 8-19. The Remove( ) method removes items from a collection by position or by key

See Also

Recipes 8.17, 8.18, and 8.20 show other features of collections.

8.20. Iterating Through a Collection

Problem

You want to process all the items in a collection one at a time.

Solution

Sample code folder: Chapter 08Collections

Use a For Each loop, or use the collection’s Count property in a For…Next loop.

Discussion

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.

Items in a collection can be accessed with For Next or For Each loops or by the item keys
Figure 8-20. Items in a collection can be accessed with For Next or For Each loops or by the item keys

See Also

Recipes 8.17, 8.18, through 8.19 show other features of collections.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.42.134