NOTE: This document was originally written for Visual dBASE 7.0/7.01, it has been updated for dB2K (release 1) and later versions of dBASE to include information about new properties, events, etc., and any new controls since the document was first written. If a control is new it will be noted. In updating this document, the images were left "as is" unless it was felt to be absolutely necessary to change them ...
In addition, this document refers to dB2K a lot, but unless it is about a dB2K specific aspect, the text can be used for Visual dBASE 7.0 through Visual dBASE 7.5.
This HOW TO document is aimed at:
Nearly all of the methods discussed in this HOW TO file have corresponding dBASE Functions (usually the same name with "A" tacked onto the front of the method name).
Arrays, like all objects, have methods associated with them. I will cover these methods in groups, based on their purposes, and will attempt to cover the corresponding functions for those more comfortable with using functions.
In a couple of cases, I will discuss a function that does not have an associated method. The main difference is that a method is called this way:
aArrayName.methodname(parameters)
While a function is called this way:
aFunction(aArrayName,parameters)
NOTE: In an attempt to make this as useful as possible, there are a couple of quick and dirty programs included with this document used to display the contents of an array. The first is ONLY useful for single-column arrays. The second should not be used for single-column arrays due to the method used to determine the array's size. To use either of them:
do show1 with aCity
do show2 with aCity
aMyArray = new array()
This tells dBASE that "aMyArray" will be an array, with all of the methods associated with one, but it does not give any sort of starting values (number of rows, columns, dimensions ...).
You can get more explicit, and tell dBASE that your array will start with 10 rows and 2 columns, by using:
aMyArray = new array(10,2)
The Procedural Method
To do this using the older procedural code, you would use:
declare aMyArray[10,2]
The advantage to the first method is that it is object-oriented, and the longer you use dBASE, the more you will find yourself using these forms of coding syntax.
The Literal Array Method
You can also define an array this way (which is useful for one- dimensional
arrays -- i.e., one column, but not for multi-dimensional), if you know what
the values will be in advance:
aMyArray = {"element1","element2","element3","etc."}
The ADD() Method
This is the most elegant way to add elements to a one-dimensional (single-column)
array. The array must already be instantiated (using one of the methods shown
above), but once you have done this, you can add elements very easily:
aMyArray.Add("value")
Where "value" is the value you wish to add to the array (note: a value in this example may be character, numeric, logical, date, array ...).
If you have an array that contains a list of cities, for example, you might want to add another city to the list. Assume the following array (called aCity):
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York
To add "Sydney" to the list:
aCity.Add("Sydney")
When this has been executed, the array will now look like:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney
The GROW Method (array.Grow()
and aGrow() )
If you have a multi-columnar array, the Add method will not work properly. It
will allow you to add items in the first column, but not any others. You should
consider, instead, using the GROW() method to handle this.
Syntax: arrayname.Grow(<type>) where <type> = 1 for a row 2 for a column
Example -- if we have this array:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000
And we want to add elements to both columns:
aCity.Grow(1) // add a row aCity[5,1] = "Sydney" aCity[5,2] = " 2,000,000"
Reminder -- the ROW is the first part of the subscript, and the COLUMN is the second. In a situation like this, if you do not know how many rows are currently in the array, you might want to use the function aLen(), to ensure you are in the correct row:
aCity.Grow(1) // add a row // number of rows in array nRow = aCity.subscript( aCity.size, 1) aCity[nRow,1] = "Sydney" aCity[nRow,2] = " 2,000,000"
Which would give us an array that looks like:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000
The GROW() method is useful as well if you wish to add a column to your array. Backing this up a step, if we had aCity defined as a single-column array:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney
and we wanted to add a second column, we could do this by using the GROW() method:
aCity.Grow(2) // add a column
Of course, at this point, the array would look like:
Column 1 Column 2 Row 1 San Francisco false Row 2 Los Angeles false Row 3 Miami false Row 4 New York false Row 5 Sydney false
Which means we would want to add data to the column. This could be done with:
aCity[1,2] = "4,000,000" aCity[2,2] = "10,000,000" aCity[3,2] = "8,000,000" aCity[4,2] = "25,000,000" aCity[5,2] = "2,000,000"
This will give us the array:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000
The INSERT() Method
(and aInsert() )
Sometimes it is desireable to add an element to an array at a location other
than the end of the array.
To do this, you would want to use the INSERT method to place the element(s) into the array at the appropriate location(s).
Syntax: arrayname.Insert(<position>[,<type>]) where <position> = position you wish to insert at <type> = 1 for a row (optional for a row) 2 for a column
Working with a single column array, assuming your array looked like the following:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 New York Row 4 Sydney
And you wanted to insert a row at 3, that contained "Miami":
aCity.Insert(3)
This will give you an array that looks like:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 false Row 4 New York
And then you would need to change the value of the element in row 3:
aCity[3] = "Miami"
NOTE: the last row now contains "New York", and what you would expect to be the last row, "Sydney" is missing. This is because the Insert() method (and the AINSERT() function) literally insert something into the current array, but do not resize it. This is as designed.
In order to make the array one row larger, as well as inserting a row into the array, you would need to use the Grow() method (or AGROW() function):
aCity.Grow(1) // 1 for row, 2 for column aCity.Insert(3) // new row at 3 aCity[3] = "Miami"
Which would produce:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 Miami Row 4 New York Row 5 Sydney
If you are working with a multi-column array, you might wish to insert a new column into the array. This would be done in a similar fashion. If you had an array that looked like:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 Miami 8,000,000 Row 4 New York 25,000,000 Row 5 Sydney 2,000,000
And you wanted to shift column 2 to the right, making it column 3, and have a new column 2 that you could use for some other set of values:
aCity.Grow(2) // add a new column aCity.Insert(2,2) // insert a new column at column 2
This would give an array that looked like:
Column 1 Column 2 Column 3 Row 1 San Francisco false 4,000,000 Row 2 Los Angeles false 10,000,000 Row 3 Miami false 8,000,000 Row 4 New York false 25,000,000 Row 5 Sydney false 2,000,000
And again, you could replace the values in the new column:
aCity[1,2] = whatever etc.
FILL() (and aFill()
)
Fill is used to place values into an array -- filling an array with default
values, for example.
Syntax: arrayname.Fill(<value>[,<start>[,<numelements>]]) where <value> = value to fill the array with <start> = (optional) start position <numelements> = (optional) number of elements to fill
Example -- If you were using an array to total some values, you might want to default everything in the array to 0:
aTotal = new array(20) aTotal.Fill(0)
You can specify a starting location, so that if you only wanted the last 15 elements to be filled, you could specify:
aTotal.Fill(0,6) // start at element six
You can also specify the number of elements to fill. If you wanted just the first five elements set to 0:
aTotal.Fill(0,1,5)
Directory Listings --
DIR() and DIREXT() (and the aDir()/aDirExt() functions)
For some situations (we will look at ComboBoxes and Listboxes in a bit), it
is desirable to grab information from the directory. You may wish to give your
user an option to open a specific table, for example. To do that, it is useful
to give them a list of all tables in the directory.
This can be done with the DIR() and DIREXT() methods.
Syntax: arrayname.Dir(<skeleton>) or arrayname.DirExt(<skeleton>) where <skeleton> = file skeleton -- you can specify the file type, for example, using standard DOS wildcards (*, ?).
Example:
aTables = new array() aTables.Dir("*.DBF")
Will get you all the .DBF files in the current directory. The only problem is, this will return the following columns:
1 2 3 4 5 Filename Size Date Time File Attributes (DOS) Character Numeric Date Character Character
What if all you really wanted was the filenames?
One simple solution is to copy the elements from the first column to another array:
aTable1 = new array() aTable1.Dir("*.DBF") aTable2 = new Array() // number of rows in first array for nRow = 1 to aTable1.subscript( aTable1.size,1) aTable2.Add(aTable1[nRow,1]) // add just the element in the // first column next
The second array will contain only the file names.
There is a shorter option, however, and that's to use the resize() method to change the number of columns in the array.
aTable = new Array() aTable.Dir("*.DBF") aTable.resize( aTable.subscript(aTable.size, 1) , 1, 1 ) // the first parameter (aTable.subscript(aTable.size, 1) ) // is the value of the number of rows in the array // the second parameter is the # of columns to keep, // if the third parameter is "non zero" we do not // keep the values in the lost columns
You may wish to allow the user to see the "Short Filename". To get this information, instead of using the DIR() method, use the DIREXT() method:
aTable1.DirExt("*.DBF")
This will return an array with the same information as the DIR() method, but it will add additional columns to the right:
6 7 8 9 Filename "Alias" Date Created Time Created Last Access Date DOS Filename Date Character Date (Short name)
The first column will hold the Windows "long" filename.
Storing the Records
(or just certain fields) in a Table into an Array
NOTE: the following are "XDML" (XBase DML commands) -- there is no equivalent
for these using the "OODML (Object Oriented DML).
COPY TO ARRAY <arrayname> [FIELDS <fieldlist>] [<scope>] APPEND FROM ARRAY <arrayname> ... REPLACE FROM ARRAY <arrayname>
These commands allow you to copy a record (or group of records) into an array for processing, and to copy the contents of an array (back) to a table.
There are two ways to copy data from a table to an array. If you define an array that is one dimensional, you will only copy one record to the array -- but you must define the number of fields to be copied -- for example, if you wanted just the first five fields:
aMyArray = new array(5) copy to array aMyArray
Will copy the first five fields of the current record to the array aMyArray. Note, that this effectively stores your record in a single column.
If you define an array that has two columns (or more, but only two will be used by this command), you must define the number of records (which equates to the number of rows in the array) and the number of fields (which equates to the number of columns in the array).
The commands:
aMyArray = new array(2,3) copy to array aMyArray
will copy the first three fields of the current record and the next record in the table (two records). This is different than the first example, because dBASE actually stores the data in the array as if it were a table -- the rows are the records, the columns are the fields (sort of like a BROWSE in dBASE).
If you wanted to copy all records and all fields in a table, you might want to use a combination of the RECCOUNT() and FLDCOUNT() functions with the instantiation of the array:
aMyArray = new array(reccount(),fldcount()) copy to array aMyArray
In addition, you can specify specific fields to copy:
aMyArray = new array(reccount(),1) // record count, 1 column copy to array aMyArray fields FirstName
You can also specify a
"scope" with the "NEXT
There are quite a few options for this, check the Online Help for COPY TO ARRAY.
Coding Your Own
There are many ways to code a routine to fill an array. Below are some examples:
// each of these examples assumes that the table was opened // using the new OODML syntax: queryname.rowset.first() // Make sure we're at the top of the table for i = 1 to queryName.rowset.count() arrayname.Add(queryname.rowset.fields["fieldname"].value) queryname.rowset.next() next queryname.rowset.first() // Make sure we're at the top of the table do while not queryName.rowset.endOfSet arrayname.Add(queryname.rowset.fields["fieldname"].value) queryName.rowset.next() enddo
To delete an element
in a single-dimensional array:
Syntax: arrayname.Delete(<element>) where <element> = element number to delete or if using optional type parameter the row or column to delete
For example, in the aCity array, which looks like:
Column 1 Row 1 San Francisco Row 2 Los Angeles Row 3 New York Row 4 Miami Row 5 Sydney
If we wanted to remove the second element:
aCity.Delete(2)
The array would contain the following values after executing this command:
Column 1 Row 1 San Francisco Row 2 New York Row 3 Miami Row 4 Sydney Row 5 false
To delete a row (single
or multiple dimensional array):
arrayname.Delete(nRow,1)
Assuming a version of the aCity array that looked like:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 New York 25,000,000 Row 4 Miami 8,000,000 Row 5 Sydney 1,000,000
To delete the second row of this array, the command:
aCity.Delete(2,1)
Would change the array to the following:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 New York 25,000,000 Row 3 Miami 8,000,000 Row 4 Sydney 1,000,000 Row 5 false false
To delete a column (multiple
columnar array):
arrayname.Delete(nColumn,2) // 2 means "column"
Assuming the aCity array looks like:
Column 1 Column 2 Column 3 Row 1 San Francisco 4,000,000 678 Row 2 Los Angeles 10,000,000 123 Row 3 New York 25,000,000 456 Row 4 Miami 8,000,000 678 Row 5 Sydney 1,000,000 1234
The command to delete the second column would be:
aCity.Delete(2,2)
Which would give us an array that looked like:
Column 1 Column 2 Column 3 Row 1 San Francisco 678 false Row 2 Los Angeles 123 false Row 3 New York 456 false Row 4 Miami 678 false Row 5 Sydney 1234 false
Removing the deleted
elements/rows/columns of the array:
In all three cases shown, the problem is, as noted, the element/row/ column
deleted will still exist in the array.
So, you then use the aReSize function/method:
Syntax: arrayname.Resize(<numrows>[,<numcolumns>]) where <numrows> = new number of rows for array <numcolumns> = (optional) new number of columns for array, if multi-column array. If using this optional parameter, the number of rows parameter must have a value greater than zero.
The following examples are combined code for the previous examples:
Removing an element from a single-dimension array:
aCity.Delete(2) nRows = aCity.subscript(aCity.size,1) aCity.Resize(nRows-1)
Removing a row of a multi-column array:
aCity.Delete(2,1) nRows = aCity.subscript(aCity.size,1) aCity.Resize(nRows-1)
Removing a column from a multi-column array:
aCity.Delete(2,2) nColumns = aCity.subscript(aCity.size,2) nRows = aCity.subscript(aCity.size,1) // Note, the ROWS parameter here // _must_ contain a value > 0. aCity.Resize(nRows,nColumns-1)
The dBASE function aCopy() is useful here, as it allows you to copy the contents of one array to another. The simplest way to do this is to ensure that the "target" array has been instantiated and is big enough to hold the contents of the first.
Syntax: aCopy(<SourceArray>,<TargetArray>) where <SourceArray> = the array you are copying from <TargetArray> = the array you are copying toIf you are copying a one-dimensional array:
nRows = aCity.subscript(aCity.size,1) aBackup = new array(nRows) aCopy(aCity,aBackup)
If you need to copy a multi-column array:
nRows = aCity.subscript(aCity.size,1) nCols = aCity.subscript(aCity.size,2) aBackup = new array(nRows,nCols) aCopy(aCity,aBackup)
In some situations, you may want to only copy some of the data from one array to another. The aCopy() function has parameters to handle this (see the Language Reference for details). In addition, you may simply wish to code your own routine.
Basically, this method returns the element of the item you are scanning for, if it is contained in the array.
Syntax: arrayname.scan(<itemtofind>) where <itemtofind> = item being searched for.
For example, if you wanted to look in the (one-column version of the) aCity array for the city "Miami", the scan() method can tell you which row it is in:
nFoundRow = aCity.Scan("Miami") ? aCity[nFoundRow]
The SCAN() method is case-sensitive.
If you are using this with a multi-column array, it gets more complex, as you will be given the element number, not the row. To make this easier, you can combine the returned value with the SUBSCRIPT() method.
nElement = aCity.Scan("Miami") nRow = aCity.Subscript(nElement,1) nColumn = aCity.Subscript(nElement,2) ? aCity[nRow,nColumn]
Syntax: arrayname.sort([<column>]) where <column> = (optional) column to sort on. If left blank, the sort method will sort on the first column.
Example:
aCity.Sort()
This sorts the array aCity on the first column. If there are more columns, the rows will all move with the elements in the first column.
Assume the following array before the sort:
Column 1 Column 2 Row 1 San Francisco 4,000,000 Row 2 Los Angeles 10,000,000 Row 3 New York 25,000,000 Row 4 Miami 8,000,000 Row 5 Sydney 1,000,000
And after:
Column 1 Column 2 Row 1 Los Angeles 10,000,000 Row 2 Miami 8,000,000 Row 3 New York 25,000,000 Row 4 San Francisco 4,000,000 Row 5 Sydney 1,000,000
To sort on the second column:
aCity.Sort(2)
Which would give us:
Column 1 Column 2 Row 1 Sydney 1,000,000 Row 2 San Francisco 4,000,000 Row 3 Miami 8,000,000 Row 4 Los Angeles 10,000,000 Row 5 New York 25,000,000
Element()
This method is useful to determine the element number of a specific element
in an array.
Syntax: arrayname.Element(<elementnumber/row>[,<column>]) where <elementnumber/row> = the element number, or the row when using a multi-dimensional array. <column> = (optional) column number
If you're working through an array in some form of processing, you could find the specific element with:
nElement = aCity.Element(1)
In a one-dimensional array, the ELEMENT() method is redundant -- it's much more useful in a multi-dimensional array. The parameters allow you to specify subscripts -- for example, in a two-column array, you could specify the row and the column:
?aCity.Element(3,2) // row 3, column 2
This should return a value of 6, as this would be the sixth element in this array. If the array had more columns, the value returned would not be 6. For example, in a three column array, using the same command above, would return a value of 8.
Subscript()
This can be really useful. If you know the element number, you can determine
the specific subscript for a multi-column (two-dimensional) array.
Syntax: arrayname.Subscript(<element>,<type>) where <element> = element number you are looking for the subscript for <type> = 1 for row, 2 for column.
Example:
?aCity.Subscript(nElement,1) // row ?aCity.Subscript(nElement,2) // column
This will give you the actual subscript (row/column) information. You could, instead, display the contents of a specific element, which can be useful if you used the Scan() method:
nElement = aCity.Scan("25,000,000") nRow = aCity.Subscript(nElement,1) nColumn = aCity.Subscript(nElement,2) ? aCity[nRow,nColumn]
aLen()
A rather useful function in dBASE, it returns the length of an array, either
by rows, columns, or number of elements. You can use the subscript method to
get the same functionality, though ...
Comboboxes and Listboxes
Comboboxes and Listboxes can use arrays as the datasource.
One caveat -- if you try to use a multi-dimensional or multi-column array, you may find your lists looking a bit strange. Example:
Column 1 Column 2 Row 1 Sydney 1,000,000 Row 2 San Francisco 4,000,000 Row 3 Miami 8,000,000 Row 4 Los Angeles 10,000,000 Row 5 New York 25,000,000
If you set your datasource property to read: "ARRAY aCity", where "aCity" is an array that looks like the above, what you will get in your combobox or listbox display is:
Sydney 1,000,000 San Francisco 4,000,000 Miami 8,000,000 Los Angeles 10,000,000 New York 25,000,000
The chances are, this is not what you wanted to happen. (A possible solution is to copy just the first column of the array into another array ...)
In addition, one problem that sometimes drives developers a bit crazy is, "Where do you define your array so that the forms designer doesn't kill it, and it is available when you run the form?"
The reason this question comes up is that when you are designing a form, if the array does not exist, the Forms Designer will allow you to continue. However, if you run the form without the array in memory, you will get an error.
So, where do you want
to define your array?
A lot depends on where you are getting the data for the array. If the array
is a hard-coded array (meaning that the values in it never change), and the
only use you have for the array is the list (meaning you do not need to perform
any lookups in the array in any other code in your form), you should use a literal
array. If the array is coming from a field in a table, you should define this
information when the form opens (using the OnOpen method).
The Literal ArrayDefining a DataSource Without Using a Literal Array
You can define your literal array either by hand, or by using the built-in visual array designer. To do it by hand, in the DataSource property, type:
ARRAY {"San Francisco","Los Angeles", etc.}To use the visual array designer, click on the tool button by the DataSource Property. This will bring up the designer. This is so easy to use that there is no real need to discuss it -- the buttons and lists are very straight-forward.
The one drawback to a literal array defined in this fashion is, there is NO name for the array. If, for any reason, you need to look at the contents, it will take some extra work on your part (one method is to actually have dBASE show the value of the datasource: form.combobox1.datasource -- you can store this into a character memory variable and parse through it, but it's not the most efficient method of working with an array).
If you do need to use the datasource array elsewhere in your code, do NOT use a literal array.
Two places immediately jump to mind:
In the Form's Open or ReadModal
event (you would need to override them)
In the Object's Open event
In either case, it is a good idea to make the array a custom property of either the form or the object. Otherwise, as soon as you leave the method, the array is no longer in scope (you will get errors that claim the array does not exist) -- of course you could make the array "PUBLIC", but this causes problems, such as remembering when you close the form to release your array(s).
To do this, when you instantiate the array, you simply add the object reference to the array. Remember -- an array is just an object, but in this case, you can make the array a property of another object as well!
form.aCity = new array() or form.ComboBox1.aCity = new array()
The only drawback is that you must refer to the full "path" to the array any time you need to reference it. The advantage is, you do not need to make this a public array (PUBLIC aCity), and do not need to worry about releasing the array from memory when you are done with it. When you close the form, the array object will disappear as well, since it is a property of the form -- this makes managing memory a snap!
Once you have instantiated the array, you can then add elements to it as you need to (this is just one example -- see earlier in this HOW TO for lots of other methods of filling an array):
form.aCity.Add("San Francisco") form.aCity.Add("Los Angeles") form.aCity.Add("Miami") form.aCity.Add("New York") form.aCity.Add("Sydney")
When you do this, do not set the datasource until after you have filled the array. Then add the statement:
form.Combobox1.Datasource = "ARRAY form.aCity" or form.Combobox1.Datasource = "ARRAY form.Combobox1.aCity"
What Happens If the
Array Changes?
In some processing that takes place, the contents of an array may change. dBASE
does not always display the changes, and this can be rather confusing. The simple
fix is to add the following statement after you have updated the contents of
the array:
form.Combobox1.Datasource = form.Combobox1.Datasource
This forces dBASE to update the datasource itself, and this will cause the display to update as well (this is called "reasserting the datasource").
TabBoxes
TabBoxes, while being a rather useful tool for multi-page forms, do not require
a complex array design for the datasource. All you really need is a literal
array, unless for some reason you need to refer to the contents of the array
later. Most of the time, this is not needed.
However, a posting on the DBASE newsgroups by a dBVIPS member showed me a rather simple and elegant method of using a literal array for the datasource for the TabBox control, and being able to use the text of the currently selected tab. Try the following in the code associated with the TabBox's OnSelChange method:
PROCEDURE TabBox1_OnSelChange // if doing multiple pages: form.PageNo := this.CurSel // store the literal array into a temporary array, // removing the word "array" from the datasource ...: aTabArray = right(this.datasource,aLen(this.datasource)-6)) // store the current tabbox text to a custom property // of the tabbox control: this.TabText = &aTabArray.[this.CurSel]
If you need to know the text at some point, it will be contained in this custom property. An example or test would be to add to this code:
msgbox(this.TabText)
Which would show the contents of the current tab's text ...
DISCLAIMER: the author is an employee of dBASE, Inc., but has written this on his own time. If you have questions regarding this .HOW document, or about dBASE you can communicate directly with the author and dBVIPS in the appropriate newsgroups on the internet..HOW files are created as a free service by members of dBVIPS and dBASE, Inc. employees to help users learn to use dBASE more effectively. They are edited by both dBVIPS members and dBASE, Inc. Technical Support (to ensure quality). This .HOW file MAY NOT BE POSTED ELSEWHERE without the explicit permission of the author, who retains all rights to the document.
Copyright 2001, Kenneth J. Mayer. All rights reserved.
Information about dBASE, Inc. can be found at:
http://www.dbase.com
EoHT: ARRAY2.HTM -- January 30, 2001 -- KJM