It's Friday. It's 7am. I have at least another half an hour before my higher brain functions begin to react to the coffee. It's tutorial time.


In this outing I'm going to be looking at the basis of all real programming - arrays. Every programming language uses arrays. An array is a declared variable in a program that can hold many values. The individual values held within the variable are accessed using the ordinal of the values position within the array eg. variable(0). In this instance the ordinal, 0, would return the first (arrays are 0 based) value in the array variable.

Static arrays

Think of a single dimension array as a list. Consider the following snippet:
dim fruit(2)
fruit(0) = "apple"
fruit(1) = "orange"
fruit(2) = "banana"
This snippet declares the array fruit as having 3 values (0 through 2) and sets a value for each ordinal. This is a static array because the number of items that the array hold is fixed by the DIM statement. But in the real world we seldom have a use for static code such as this. More often we want arrays to hold an number of values that can only be determined at runtime.

Single dimesion arrays

Let's crete our array of fruits again but this time make it dynamic, so that it can hold an indeterminate number of items.
'Declare our array
fruit = array

'Add a new value to our array.
redim preserve fruit(ubound(fruit)+1)
fruit(ubound(fruit)) = "apple"
redim preserve fruit(ubound(fruit)+1)
fruit(ubound(fruit)) = "orange"
redim preserve fruit(ubound(fruit)+1)
fruit(ubound(fruit)) = "banana"
What just happened? Ok - first we declared our array. Using the syntax above the array is created empty. To add the first item to our list we need to tell the array to make a space for our new value. This is done using the REDIM statement, which re-dimensions the array. By using the PRESERVE keyword we tell the array to remember all previously stored values. If we ommit the preserve keyword any data that already exists in our array is lost.

We can find out how many values an array holds using the UBOUND keyword. UBOUND(anArray) tells us how many items there are in teh array anArray. Because arrays are 0 based the number of items in a single dimesion array always equals UBOUND(anArray)+1. So the value of fruit(ubound(fruit)+1) will equal 0, the number of items currently held in the array fruit +1. The line fruit(ubound(fruit)) = "apple" sets the new value that we have created (at ordinal 0) in the array to "apple".

redim preserve fruit(ubound(fruit)+1)
means create a new value 1 greater than the current number of items in the array, keeping all the existsing values and:
fruit(ubound(fruit)) = "apple"
Sets the value of the newly created item.

Multidimensional arrays

You can have arrays with many dimensions. Imagine a 2 dimensional array as a table. Imagine a 3 dimensional array as a cube. Try not to imagine a number of dimensions large than 3 however because it is the prime cause of spontanious human combustion. I think you can have a riduculous number of arrays in VB (20? maybe) but certainly enough that you could never use them. Arrays with more than 3 dimensions are very rarely used because the human mind has difficulty thinking in more than 3 dimensions. If someone ever asks you for more than 3 dimensions ask for mental insurance. You'll need it. But I digress.

Once again let's start with the static version:
'It's a table - 3 cells wide by three cells high
'Think of this as x,y axis
dim aTable(2,2) 

atable(0,0) = "0,0"
atable(0,1) = "0,1"
atable(0,2) = "0,2"
atable(1,0) = "1,0"
atable(1,1) = "1,1"
atable(1,2) = "1,2"
atable(2,0) = "2,0"
atable(2,1) = "2,1"
atable(2,2) = "2,2"
Once again this code is not very useful because it is static. So how can we make it dynamic? When using multi dimensional arrays we can still use our UBOUND keyword. With multidimesional arrays the syntax of the UBOUND is slighly different however:
Where anArray is the array that you wish to find the itemcount for and dimension is the dimension number.

An example. To get the upper bound of our x and y dimensions above we would use the following:
We cannot however us the PRESERVE keyword when re-decalring multi dimesional arrays and so the dynamic version of the above code is a bit more sticky. We can however get round the problem by writing a function that:
1...Creates a tempoary array of the right dimensions
2...Copies the old data into the tempoary array
3...Adds the new data to the tempoary array
4...Returns the tempoary array.

The function might look like this:
aTable = array
aRow = array

aRow = ("Apples","Green","pips")
aTable = AppendRecord(aTable,aRow)

aRow = ("Oranges","Orange","pith")
aTable = AppendRecord(aTable,aRow)

aRow = ("Bananas","Yellow","peel")
aTable = AppendRecord(aTable,aRow)

for y = 0 to UBOUND(aTable,1)
	for x = 0 to UBOUND(aTable,2)
		echo aTable(y,x) ' Think (Row,Column)

Public Function AppendRecord(p_Table,p_Record)

	temp = array
	'p_table is a 2 dimesional array
	'p_Record is a 1 dimesional array

	'p_record will be treated as a row and added to the 
	'end of the p_table array
	'Create an empty array of correct new dimensions
	redim temp(ubound(p_Table,1)+1, ubound(p_record))
	'Add the p_Table values to temp
	for y = 0 to ubound(p_Table,1)
		for x = 0 to ubound(p_Table,2)
			'Copy the value at cell y,x from p_Table to temp
			temp(y,x) = p_Table(y,x)
	'**Create a new record in the array
	'Iterate through fields in passed array adding to the temp array
	for n = 0 to ubound(p_record)
		temp(ubound(temp,1),n) = p_record(n)
	'Set the new values in the p_Table Array
	AppendRecord = temp

End Function
Directional sorting of 2 dimensional arrays

And now, for those of you who havn't given up, here's an ASP class that I use to sort 2 dimensional arrays (tables). Once you've got your 2 dimensional array (or table) you will probably want to be able to sort it, either into ascending or decending order, and using the column ordinal to sort by. This code is written in a class so that I can instantiate a copy of it for within any other script without re-writting this code.

		aArray - 2 dimensional array
		OrderBy - Ordinal of column to sort by - default 0 
		Dir - Direction to sort in ("ASC"/"DESC") - default "asc"
The class file!

'<----- VBScript Class By NTSA
'<----- Class Name : ArrayCls
'<----- Compile Date : 11/09/2002

Class ArrayCls

Public function SortArray(aArray,OrderBy,Dir)
	'aArray - 2 dimensional array
	'OrderBy - Ordinal of column to sort by
	'Dir - Direction to sort in (ASC/DESC)

	'If sort direction is ommitted or not asc or desc then set to asc
	dir = lcase (dir)
	if dir = "" or (dir<>"asc" and dir<>"desc") then dir = "asc"
	if len(orderby) = 0 then orderby = 0

	san = 0
		changed = false

		for row = 0 to ubound(aArray,1)-1
		select case AlphaCompare(aArray(row,OrderBy),aArray(row+1,OrderBy))
			case 0
				'Both the same
			case 1
				'row row is alpha< than row row+1
				IF dir = "desc" then
					aArray = SwapRows(aArray,row,row+1)
					changed = true
				end if
			case 2
				'row row+1 is alpha< than row n
				if dir = "asc" then
					aArray = SwapRows(aArray,row,row+1)
					changed = true
				end if
		end select
		san = san + 1

	loop until changed = false or san = ubound(aArray,1)

	IF san => (ubound(aArray,1)*ubound(aArray,1)) THEN 
		response.write "failed sanity check!
	end if

	SortArray = aArray

end function

'**Private functions

private function SwapRows(aArray,row1,row2)

	temp = array

	'Copy aArray into a temp variable
	redim temp(ubound(aArray,1),ubound(aArray,2))
	for y = 0 to ubound(aArray,1)
		for x = 0 to ubound(aArray,2)
			temp(y,x) = aArray(y,x)

	'Iterate throgh the colums
	for n = 0 to ubound(aArray,2)
		'Swap to row values
		temp(row1,n) = aArray(row2,n)
		temp(row2,n) = aArray(row1,n)

	'Return the new array
	SwapRows = temp

end function

private function AlphaCompare(val1,val2)

	'Compare val1 and Val1
	' - returns 0 for same
	'	1 if val1 is top
	'	2 if val2 is top

	val1 = lcase(trim(val1))
	val2 = lcase(trim(val2))

	if val1 = val2 then 
		AlphaCompare = 0
		exit function
	end if

	len1 = len(val1)
	len2 = len(val2)

	if len1 < len2 then
		checkchars  = len1
		'len1 is shorter so the default 
		'(if everything else the same) is 1
		AlphaCompare = 1
		checkchars  = len2
		'len1 is shorter so the default 
		'(if everything else the same) is 2
		AlphaCompare = 2
	end if

	FOR n = 1 to checkchars
		checkchar1 = asc(mid(val1,n,1))
		checkchar2 = asc(mid(val2,n,1))
		if checkchar1 < checkchar2 then
			AlphaCompare = 1
			exit function
		end if
		if checkchar2 < checkchar1 then
			AlphaCompare = 2
			exit function
		end if

end function

End Class