Ask anyone in the software business and you’ll soon learn that



Lesson 12. Working with Two Dimesnional Arrays

In the previous chapter single dimension arrays were covered in depth. In this chapter we extend those same concepts but apply them to a two dimensional array covered in an extended example.

The example we use covers an application being developed by Mary which requires the analysis of heating degree day data supplied to Natural Gas Traders. Mary does not have the actual data so she develops a test data set before she begins her design. She checked the data set shown below with her client and the client agreed that the data is realistic.

Further she was told that the data changed monthly so she decided to display the data in a listbox since the data will be used online.

Oct Nov Dec Jan Feb Mar Apr

NW 1250 1366 1498 1789 1698 1566 0875

NE 1135 1233 1566 1976 2043 1932 1010

SW 0567 0678 1101 1292 1366 1302 0773

SE 0433 0566 1002 1109 1229 1154 0677

The users need to see the raw data shown above but additional data is required as shown in the sample layout they provided.

Regional Natural Gas Consumption

Previous Heating Season

(bil Cu. Ft)

Oct Nov Dec Jan Feb Mar Apr Total High Low Mean Median

NW 1250 1366 1498 1789 1698 1566 0875 xxxx xxxx xxxx xxxx xxxx

NE 1135 1233 1566 1976 2043 1932 1010 xxxx xxxx xxxx xxxx xxxx

SW 0567 0678 1101 1292 1366 1302 0773 xxxx xxxx xxxx xxxx xxxx

SE 0433 0566 1002 1109 1229 1154 0677 xxxx xxxx xxxx xxxx xxxx

--------------------------------------------------------------------------------

Total xxxx xxxx xxxx xxxx xxxx xxxx xxxx G,GGGG

High xxxx xxxx xxxx xxxx xxxx xxxx xxxx HHHH

Low xxxx xxxx xxxx xxxx xxxx xxxx xxxx LLLL

Mean xxxx xxxx xxxx xxxx xxxx xxxx xxxx MEAN

Median xxxx xxxx xxxx xxxx xxxx xxxx xxxx MEDIAN

Essentially the traders want to be able to click an icon and have the above table displayed on their screens.

The row and columnar data in the body of the report is clear, but Mary was confused about the lower right corner of the report. The traders cleared up the confusion by defining the placeholders.

G,GGG The total of the 28 Heating Degree Day(HDD)Values.

HHHH The High of all Highs

LLLL The Low of all Lows

MEAN The mean of the 28 raw data values

MEDIAN The median of the 28 raw data values

Since this application seems somewhat more difficult than the application in the previous chapter, we suggest a divide and conquer approach.

So listing tasks and any issues that arise seems to be a good first step. The following is a reasonable list.

Task 1 Initialize variables.

Task 2 Get the raw data from the file and validate the data by dropping it into a listbox.

Task 3 Revise Task 2 by converting the text data into numeric format and display it in a listbox or send it to Debug.WriteLine. (This assures we get the variables of interest.)

Task 4. Revise 3 so that we get the data into into a 2D array.

Task 5. Compute the statistics.

Task 6. Build and display the data in the listbox.

Clearly this application has a number of distinct steps, so Mary immediately decides to build a small collection of methods that would be called from within the form load event. The application does seem quite clear so she decides to immediately write code instead of building PDL first. As far as application documentation, she will let the documentation be the code !

Task 2 Get the raw data from the file and validate the data by dropping it into a listbox. With nothing new, the code in Fig 1. below accomplishes this task. But the data is simply string data and certainly not in the desired array. After execution the list box does contain the data as shown in Figure 2.

Imports System.IO

Public Class frmMain

Dim H1, H2, H3, H4 As String

Private Sub frmMain_Load(ByVal sender As System.Object,

ByVal e As System.EventArgs) Handles MyBase.Load

GetHeatingDegreeDayData()

End Sub

Sub GetHeatingDegreeDayData()

Dim sInputFilePath As String = "c:\HDD.txt"

Dim fileIn As New StreamReader(sInputFilePath)

Dim sRecIn As String

' Get the Heading line from the File

sRecIn = fileIn.ReadLine()

H4 = sRecIn

' Get the detail records from the file

Do Until sRecIn Is Nothing

ListBox.Items.Add(sRecIn)

sRecIn = fileIn.ReadLine()

Loop

End Sub

Figure 1. Task 1 code

[pic]

Figure 2. The raw data in the original string format.

Well, that certainly looks promising, but Mary needs to capture the 28 numeric values from the string data. Once she can grab each value, she needs to drop it into the array. First she needs to define the array thus giving it a size. Nine rows by 12 columns seems reasonable to hold the numeric data. This should make sense after you reexamine the report format shown above.

Ater spending about a six hours Mary arrives at the following code segment that works as expected.

Sub GetHeatingDegreeDayData()

Dim sInputFilePath As String = "c:\HDD.txt"

Dim fileIn As New StreamReader(sInputFilePath)

Dim sRecIn As String

Dim i, j As Byte ' array indexers

Dim HDD(9, 12) As Single ' since we want 1 decimal position accuracy

' in the averages.

' Get the Heading line from the File

sRecIn = fileIn.ReadLine()

H4 = sRecIn

sRecIn = fileIn.ReadLine()

Dim sOut As String = Nothing ' use sOut to build a string to display

For i = 0 To 3 ‘ because we have three lines of numeric data

HDD(i, 0) = CSng(Mid$(sRecIn, 5, 4))

HDD(i, 1) = CSng(Mid$(sRecIn, 13, 4))

HDD(i, 2) = CSng(Mid$(sRecIn, 19, 4))

HDD(i, 3) = CSng(Mid$(sRecIn, 26, 4))

HDD(i, 4) = CSng(Mid$(sRecIn, 33, 4))

HDD(i, 5) = CSng(Mid$(sRecIn, 40, 4))

HDD(i, 6) = CSng(Mid$(sRecIn, 47, 4))

‘ SetUp sOut so we can display it.

For j = 0 To 6 : sOut += CStr(HDD(i, j)) + " " : Next

Debug.WriteLine(sOut)

sOut = Nothing ' Reset sOut

sRecIn = fileIn.ReadLine() ' Get the next line of text

Next i

End Sub

Her code is straightforward but a little bulky in that she doesn’t use a loop to grab the 7 data values in each line. When the data are not equally spaced an inner loop will not work. The good news is that she has the data in the HDD array! As she reviews her code she realizes that the HDD array is defined within the GetHeatingDegreeDayData method. This appears problematic since HDD will also be used in the statistics method. So she decides to change it from being local to being Global. To do this she cuts these lines

Dim HDD(9, 12) As Single ' since we want 1 decimal position accuracy

' in the averages.

Dim i, j As Byte ' array indexers

out of the GetHeatingDegreeDayData method and pastes them after the

Dim H1, H2, H3, H4 As String statement thus making these variable global.

Now the HDD array can be manipulated by any of the routines in the Class.

This completes Task 4.

Task 5 requires getting the statistics and placing them in the appropriate cells of the HDD array. The first thing Mary does is begins to plan how she will attack the statistics. The totals for the rows and columns seem to be straightforward and she thinks that as she makes those passes through the row and column data, she might as well find the row and column high and low values. Finally getting the averages shouldn’t be that difficult. That should be enough to work on for now, so she postpones handling the summarial totals in the bottom right corner of the report as well as the medians. In fact, she decides to get the row statistics first. Below is her first pass at the output which she immediately checks with a calculator.

1250 1366 1498 1789 1698 1566 875

1135 1233 1566 1976 2043 1932 1010

567 678 1101 1292 1366 1302 773

433 566 1002 1109 1229 1154 677

1250 1366 1498 1789 1698 1566 875

1135 1233 1566 1976 2043 1932 1010

567 678 1101 1292 1366 1302 773

433 566 1002 1109 1229 1154 677

============= Row Statistics Follow ===========

Total High Low Mean

10042 1789 875 1434.571

10895 2043 1010 1556.429

7079 1366 567 1011.286

6170 1229 433 881.4286

The row statistics are correct and her code is shown on the next page.

Imports System.IO

Public Class frmMain

Dim H1, H2, H3, H4 As String

Dim HDD(9, 12) As Single ' since we want 1 decimal position accuracy

' in the averages.

Dim i, j As Byte ' array indexers

Dim sOut As String = Nothing ' use sOut to build a string to display data

Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

GetHeatingDegreeDayData()

GetTotalsAverageHighLow()

End Sub

Sub GetHeatingDegreeDayData()

Dim sInputFilePath As String = "c:\HDD.txt"

Dim fileIn As New StreamReader(sInputFilePath)

Dim sRecIn As String

sRecIn = fileIn.ReadLine() ' Get the Heading line from the File

H4 = sRecIn

sRecIn = fileIn.ReadLine()

For i = 0 To 3

HDD(i, 0) = CSng(Mid$(sRecIn, 5, 4))

HDD(i, 1) = CSng(Mid$(sRecIn, 13, 4))

HDD(i, 2) = CSng(Mid$(sRecIn, 19, 4))

HDD(i, 3) = CSng(Mid$(sRecIn, 26, 4))

HDD(i, 4) = CSng(Mid$(sRecIn, 33, 4))

HDD(i, 5) = CSng(Mid$(sRecIn, 40, 4))

HDD(i, 6) = CSng(Mid$(sRecIn, 47, 4))

For j = 0 To 6 : sOut += CStr(HDD(i, j)) + " " : Next

Debug.WriteLine(sOut)

sOut = Nothing ' Reset sOut

sRecIn = fileIn.ReadLine() ' Get the next line of text

Next i

End Sub

Sub GetTotalsAverageHighLow()

' Iterate over columns to get the row statistics.

Dim MONTHS = 7

For i = 0 To 3

HDD(i, 8) = Single.MinValue ' Initialize High

HDD(i, 9) = Single.MaxValue ' Initialize Low

For j = 0 To 6

HDD(i, 7) += HDD(i, j) ' Build the Total Column.

If HDD(i, j) > HDD(i, 8) Then HDD(i, 8) = HDD(i, j) 'Get High

If HDD(i, j) < HDD(i, 9) Then HDD(i, 9) = HDD(i, j) 'Get Low

Next j

HDD(i, 10) = HDD(i, 7) / MONTHS

Next i

' Let's temporarily display the data

Debug.WriteLine("============= Row Statistics Follow ===========")

Debug.WriteLine(" Total High Low Mean ")

For i = 0 To 3

sOut = CStr(HDD(i, 7)) + " " _

+ CStr(HDD(i, 8)) + " " _

+ CStr(HDD(i, 9)) + " " _

+ CStr(HDD(i, 10))

Debug.WriteLine(sOut)

Next i

End Sub

End Class

Well, given she has all of the code for the row statistics, she copies and pastes this code below the row statistics code and revises it to handle the columnar totals.

The new output is shown below.

1250 1366 1498 1789 1698 1566 875

1135 1233 1566 1976 2043 1932 1010

567 678 1101 1292 1366 1302 773

433 566 1002 1109 1229 1154 677

============= Row Statistics Follow ===========

Total High Low Mean

10042 1789 875 1434.571

10895 2043 1010 1556.429

7079 1366 567 1011.286

6170 1229 433 881.4286

============= Column Statistics Follow ===========

Rows below show Total,High,Low, Mean values

3385 3843 5167 6166 6336 5954 3335

1250 1366 1566 1976 2043 1932 1010

433 566 1002 1109 1229 1154 677

846.25 960.75 1291.75 1541.5 1584 1488.5 833.75

=====================================================

Again, Mary checks the data and it is correct.

Below is the code block that she added after the row statistics code.

'===================================================================

'=========================================== Get Columnar Statistics

'===================================================================

Dim REGIONS As Byte = 4

sOut = Nothing

For j = 0 To 6

HDD(5, j) = Single.MinValue ' Initialize High

HDD(6, j) = Single.MaxValue ' Initialize Low

For i = 0 To 3

HDD(4, j) += HDD(i, j) ' Build the Total Column.

If HDD(i, j) > HDD(5, j) Then HDD(5, j) = HDD(i, j) 'Get High

If HDD(i, j) < HDD(6, j) Then HDD(6, j) = HDD(i, j) 'Get Low

Next i

HDD(7, j) = HDD(4, j) / REGIONS

Next j

' Let's temporarily display the data

Debug.WriteLine("============= Column Statistics Follow ===========")

Debug.WriteLine(" Rows below show Total,High,Low, Mean values")

For i = 4 To 7

For j = 0 To 6

sOut += CStr(HDD(i, j)) + " "

Next j

Debug.WriteLine(sOut)

sOut = Nothing

Next i

Debug.WriteLine("=====================================================")

' ============================================ End Columnar Statistics

End Sub

Next, she turns her attention to the statistics in the lower right corner of the report, namely the G,GGG HHHH, LLLL, MEAN, MEDIAN values. Each of these values fall within a cell of the HDD array. Rather than making the GetTotalsAverageHighLow() too large, she adds another method called GetOverAllStatistics() which will get the first four of the five values she needs for the lower right section of the report.

She realizes that these statistics pertain to all 28 data points. Two ways of obtaining the statistics are possible. Obtain the Low of the already computed lows, then the Highs of the already computed Highs and the average of the averages ( which isn’t quite correct and if you don’t believe it try it!) Another method is to look at the 28 values and simply compute the High, Low, and Average using the raw data that is already in the array. This is the method she chooses.

Before continuing, she decides to display all of the data in the array – 9 rows by 12 columns just to be sure all is well. She saw what she expected and then computed the overall statistics. Using the code she has already written to high, low, mean value she now iterates over all 28 values in order to generate the summarial statistics. That code is shown below.

Sub GetOverAllStatistics()

Dim HDD_OBSERVATIONS As Byte = 28

HDD(5, 8) = Single.MinValue ' Initialize Highest Value

HDD(6, 9) = Single.MaxValue ' Initialize Lowest Value

For i = 0 To 3

For j = 0 To 6

HDD(4, 7) += HDD(i, j)

If HDD(i, j) > HDD(5, 8) Then HDD(5, 8) = HDD(i, j)

If HDD(i, j) < HDD(6, 9) Then HDD(6, 9) = HDD(i, j)

Next j

Next i

HDD(7, 10) = HDD(4, 7) / HDD_OBSERVATIONS ‘ Get Overall Average

Debug.WriteLine("------------------- Full Array Listing -------------")

For i = 0 To 8

sOut = Nothing

For j = 0 To 11

sOut += CStr(HDD(i, j)) + " "

Next

Debug.WriteLine(sOut)

Next

Debug.WriteLine("------------------------------------------------------")

End Sub

Task 5 is taking quite a while to complete, but finally she reaches the last step in this task – Get the Medians ! To keep the code nicely organized she adds a GetMedians method. She know that to compute the grand median ( the value in the lower right corner of the report, she needs to get all 28 values into a single dimension array inorder to sort. Then once thay are sorted, she will take the average the center two values to obtain the grand median since we have an even number of values. Remember that the median is that value that has as many values on the left as it has on the right.

Mary wrote the code below to compute the Grand Median. Frm_Load was revised as shown.

Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

GetHeatingDegreeDayData()

GetTotalsAverageHighLow()

GetOverAllStatistics()

GetTheMedians()

End Sub

And the first part GetTheMedians() code follows.

Sub GetTheMedians()

Dim Hold(28) As Single

Dim n As Byte

' -------- Copy 28 values into Hold and find the Grand Median

For i = 0 To 3

For j = 0 To 6

Hold(n) = HDD(i, j)

n += 1

Next j

Next i

Array.Sort(Hold)

HDD(8, 11) = (Hold(14) + Hold(15)) / 2

‘ --------------------------- Now lets show the data

Debug.WriteLine("------------------- Full Array Listing -------------")

For i = 0 To 8

sOut = Nothing

For j = 0 To 11

sOut += CStr(HDD(i, j)) + " "

Next

Debug.WriteLine(sOut)

Next

Debug.WriteLine("------------------------------------------------------")

End Sub

Next Mary needed to figure out how to get the medians for each of the data rows and columns and place the medians in row 8 ( ie the months) and column 11( ie the regions).

The regions had seven data items per row and the months had four data items per column. Also, the client sent her another sample data set that they suggested she use.

She decided to build another routine to handle the medians and call it from the frmMain_Load event. The code for the median routine follows. As usual we suggest that you copy the code line for line so that you see the meaning of each line. We find that simply reading the code isn’t as effective as copying it with a pencil and paper. We also suggest that you reverse engineer the actual code back into PDL . Do this on another sheet of paper.

Sub GetTheMedians()

Dim Hold(28) As Single

Dim n As Byte

' -------- Copy 28 values into Hold and find the Grand Median

For i = 0 To 3

For j = 0 To 6

Hold(n) = HDD(i, j)

n += 1

Next j

Next i

Array.Sort(Hold)

HDD(8, 11) = (Hold(14) + Hold(15)) / 2

'----------------------------------------------------------------------

' Get the Four Row Medians

'----------------------------------------------------------------------

Dim Hold2(7) As Single

For i = 0 To 3

For n = 0 To 6 : Hold2(n) = 0 : Next n ' Initialize Hold

For j = 0 To 6 : Hold2(j) = HDD(i, j) : Next ' Fill Hold2

Array.Sort(Hold2)

HDD(i, 11) = Hold2(4)

Next i

'----------------------------------------------------------------------

' Get the Seven Column Medians

'----------------------------------------------------------------------

Dim Hold3(4) As Single

Debug.WriteLine("=================== In Medians ===============")

For j = 0 To 6

For i = 0 To 3 : Hold3(i) = 0 : Next i ' Initialize Hold

For i = 0 To 3 : Hold3(i) = HDD(i, j) : Next ' Fill Hold3

'Debug.WriteLine("===========================================")

'sOut = Nothing

'sOut = "===" + CStr(Hold3(0)) + " " + CStr(Hold3(1)) _

' + " " + CStr(Hold3(2)) + " " + CStr(Hold3(3))

'Debug.WriteLine(sOut)

'Array.Sort(Hold3)

'sOut = Nothing

'sOut = "===" + CStr(Hold3(0)) + " " + CStr(Hold3(1)) _

' + " " + CStr(Hold3(2)) + " " + CStr(Hold3(3))

'Debug.WriteLine(sOut)

'Debug.WriteLine("===========================================")

HDD(8, j) = (Hold3(1) + Hold3(2)) / 2

Next j

End Sub

Mary tested the above code to be sure the medians were correct and after a few trial and error attempts the code was correct.

Finally it was time for the last Task – Display the report. This last task proved problematic. She was using which did not have Report Writing features built in. But she thought she would give it a try and at least get all of the data on paper. Below is the code she wote to generate the report.

Sub DisplayReport()

Dim H1, H2, H3, H4 As String

Dim RowTitle(9) As String

RowTitle(0) = "NW "

RowTitle(1) = "NE "

RowTitle(2) = "SW "

RowTitle(3) = "SE "

RowTitle(4) = "Total "

RowTitle(5) = "High "

RowTitle(6) = "Low "

RowTitle(7) = "Mean "

RowTitle(8) = "Median "

H1 = " Regional Natural Gas Consumption"

H2 = " Previous Heating Season"

H3 = " ( bil Cu. Ft. )"

H4 = " Oct Nov Dec Jan Feb Mar Apr _

Total High Low Mean Median"

Debug.WriteLine(H1)

Debug.WriteLine(H2)

Debug.WriteLine(H3)

Debug.WriteLine(H4)

For i = 0 To 3

sOut = RowTitle(i)

For j = 0 To 11

' sOut += CStr(HDD(i, j)) + " "

sOut += FormatNumber(HDD(i, j), 0) + " | "

Next

Debug.WriteLine(sOut)

Next

Debug.WriteLine("------------------------------------------------------_

-----------------------------------------------")

For i = 4 To 8

sOut = RowTitle(i)

For j = 0 To 11

If HDD(i, j) = 0 Then

sOut += " | "

Else

sOut += FormatNumber(HDD(i, j), 0) + " | "

End If

Next

Debug.WriteLine(sOut)

Next

End Sub

The output for the above method is shown below. The revised data set is shown above the actual report output.

[pic]

She did a little research and found that GDI+ could be used to help build a more nicely formatted report, but it would be a time consuming process. Buying a more powerful version of was suggested and rejected. She then remembered a concept covered in an early course she took. The idea was that you could generate data and send it to a text file and then import the text file into Excel and use the power of Excel to format the data.

She also knew her clients were heavy Excel users. The code below was inserted.

‘ ================================ Send the output to the Immediate Window

For i = 4 To 8

sOut = RowTitle(i)

For j = 0 To 11

If HDD(i, j) = 0 Then

sOut += " | "

Else

sOut += FormatNumber(HDD(i, j), 0) + " | "

End If

Next

Debug.WriteLine(sOut)

Next

'===========================================================================

' Send the data to a text file

'===========================================================================

Dim sOutputFilePath As String = "c:\HDDReport1.txt"

Dim fileOut As New StreamWriter(sOutputFilePath)

Dim sRecOut As String = Nothing

For i = 0 To 8

For j = 0 To 11

sRecOut += CStr(HDD(i, j)) + ","

Next j

fileOut.WriteLine(sRecOut)

sRecOut = Nothing

Next i

fileOut.Close()

End Sub

The Excel Output is shown below.

[pic]

Mary was surprised at the amount of work it took to generate such a report but considered the effort to be a meaningful learning experience. She learned how to break a multitask project into smaller tasks and then expanded those tasks into code.

You might think that she should have started in Excel immediately, but the purpose of this exercise was of course to cover working with two dimensional arrays from within a programming language.

As an extended exercise, we suggest that you copy Mary’s code character by character using a pencil and paper, key it yourself and run it. In addition Extracting the PDL from the code is another beneficial exercise. Remember that in the real world first the PDL would be developed and validated before any code was written. Depending on the needs of the client, sometimes the PDL step might be omitted.

................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download