# Passing string value to 3-dimensional array

This is probably a really easy question but, I haven't worked with Array's in quite a while and can't remember how this is done:

I have a 3-dimensional array. I use X and Y counter variables to point to the first two locations, then put a string of text into the third dimension's location. Is there something I need to do when declaring the Array? What do I need to do in order to pass a string into an array? Should I be using a parameter array as opposed to a byte array or vice versa? Thanks for the help,

Jamauss
LVL 1
###### Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Author Commented:
I'll provide the code I'm using if neccessary.
0
Commented:
First of all, a three-dimensional array holds of a certain data type.  From what I can extrapolate from your description, it appears that you wish to store, in each element, an object which has three components.  However, it appears that you want to use arrays Dim'ed with three dimension to hold elements with three components.  This is not the way arrays work.  If that indeed the functionality you desire, you need some implementation of a vector class.  So whatever data type you Dim'ed the array as will be the type of each element stored in the array.  You evidently have done the something like the following:
Dim thearray(5, 5, 5) As Byte

Now it seems you have attempted to store an element according to the following:
thearray(1, 3, "SomeString")

Try to think of an array as a matrix of elements of a certain type.  Think of a two-dimension matrix in this case for simplicity.  So let's compare our array to a 3 x 3 matrix of integers.  This matrix stores nine elements of integers as follows:

ROW  COL 1  2  3
1     | 9  3  5|
2     | 5  2  1|
3     | 8  3  0|

So the element stored at row 1, column 1 is "9"; row 1, column 2 is "3"; ... ; row 3, column 2 is "3"; and row 3, column 3 is "0".

Now the VB array decl would be:
Dim iMatrix(1 To 3, 1 To 3) As Integer

(Note that we could have also thought of the matrix as storing people's names in place of the integers above.  In this case, the declaration would have been Dim iMatrix(1 To 3, 1 To 3) As String).

Now, we would assign the values from the above matrix as follows:
iMatrix(1, 1) = 9
iMatrix(1, 2) = 3
iMatrix(1, 3) = 5
iMatrix(2, 1) = 5
iMatrix(2, 2) = 2
iMatrix(2, 3) = 1
iMatrix(3, 1) = 8
iMatrix(3, 2) = 3
iMatrix(3, 3) = 0

And we could use the individual elements (in this case for addition), for example:
iMatrix(1, 1) + iMatrix(3, 1) = 9 + 8 = 17

Dim strNames(X, Y, Z) As String

where X, Y, and Z are each of the Long data type and index each single string element of the three-dimensional array(matrix).

Now, according to the description you've provided, what you need is an array of VB UDTs(User Defined Types).  For, if you wish to associate an X and Y value with a string, then you should declare your type as follows:

Public|Private Type udtSomeNewType
X As Integer
Y As Integer
Name As String
End Type

Now you should declare an array of such UDTs as follows:
Dim udtarray(<lowerbound> To <upperbound>) As udtSomeNewType

Now each element in this single-dimension array will contain a udtSomeNewType "object".  Here is an example of using an array of UDTs:

Dim thearray(1 To 5) As udtSomeNewType
Dim thisudt As udtSomeNewType
Dim iIdx As Integer

udtSomeNewType.X = 1
udtSomeNewType.Y = 17
udtSomeNewType.Name = "Bugs Bunny"
thearray(1) = udtSomeNewType

udtSomeNewType.X = 4
udtSomeNewType.Y = -769
udtSomeNewType.Name = "Ronald Reagan"

For iIdx = 1 To 5
Debug.Print udtSomeNewType(iIdx).X; udtSomeNewType(iIdx).Y; udtSomeNewType(iIdx).Name
Next iIdx

You'll receive the following output in the debug window:

1  17 Bugs Bunny
4 -769 Ronald Reagan
0  0
0  0
0  0

Note that elements 3, 4, and 5 default to 0, 0, and "" for the respective X, Y, and Name components of each udtSomeNewType element.
0
Commented:
scontreras changed their proposed answer to a comment
0
Commented:
It appears I posted a little too hastily and forgot to proofread my post.  Corrections are enclosed as follows: <correction>.

First of all, a three-dimensional array holds <elements> of a certain data type.  From what I can extrapolate from your description, it appears that you wish to store, in each element, an object which has three components.  However, it appears that you want to use arrays Dim'ed with three dimensions to hold elements with three components.  This is not the way arrays work.  If that indeed <is> the functionality you desire, you need some implementation of a vector class.  So whatever data type you Dim'ed the array as will be the type of each element stored in the array.  You evidently have done something like the following:
Dim thearray(5, 5, 5) As Byte

Now it seems you have attempted to store an element according to the following:
thearray(1, 3, "SomeString")

<This obviously does not work>

Try to think of an array as a matrix of elements of a certain type.  Think of a two-dimension matrix in this case for simplicity.  So let's compare our array to a 3 x 3 matrix of integers.  This matrix stores nine elements of integers as follows:

ROW  COL 1  2  3
1     | 9  3  5|
2     | 5  2  1|
3     | 8  3  0|

So the element stored at row 1, column 1 is "9"; row 1, column 2 is "3"; ... ; row 3, column 2 is "3"; and row 3, column 3 is "0".

Now the VB array decl would be:
Dim iMatrix(1 To 3, 1 To 3) As Integer

(Note that we could have also thought of the matrix as storing people's names in place of the integers above.  In this case, the declaration would have been Dim iMatrix(1 To 3, 1 To 3) As String).

Now, we would assign the values from the above matrix as follows:
iMatrix(1, 1) = 9
iMatrix(1, 2) = 3
iMatrix(1, 3) = 5
iMatrix(2, 1) = 5
iMatrix(2, 2) = 2
iMatrix(2, 3) = 1
iMatrix(3, 1) = 8
iMatrix(3, 2) = 3
iMatrix(3, 3) = 0

And we could use the individual elements (in this case for addition) <as follows>, for example:
iMatrix(1, 1) + iMatrix(3, 1) = 9 + 8 = 17

Dim strNames(X, Y, Z) As Byte

where X, Y, and Z are each of the Long data type and index each single Byte element of the three-dimensional array(matrix).

Now, according to the description you've provided, what you need is an array of VB UDTs(User Defined Types).  For, if you wish to associate an X and Y value with a string, then you should declare your type as follows:

Public|Private Type udtSomeNewType
X As Integer
Y As Integer
Name As String
End Type

Now you should declare an array of such UDTs as follows:
Dim udtarray(<lowerbound> To <upperbound>) As udtSomeNewType

Now each element in this single-dimension array will contain a udtSomeNewType "object".  Here is an example of using an array of UDTs:

Dim thearray(1 To 5) As udtSomeNewType
Dim thisudt As udtSomeNewType
Dim iIdx As Integer

udtSomeNewType.X = 1
udtSomeNewType.Y = 17
udtSomeNewType.Name = "Bugs Bunny"
thearray(1) = udtSomeNewType

udtSomeNewType.X = 4
udtSomeNewType.Y = -769
udtSomeNewType.Name = "Ronald Reagan"

For iIdx = 1 To 5
Debug.Print udtSomeNewType(iIdx).X; udtSomeNewType(iIdx).Y; udtSomeNewType(iIdx).Name
Next iIdx

You'll receive the following output in the debug window:

1  17 Bugs Bunny
4 -769 Ronald Reagan
0  0
0  0
0  0

Note that elements 3, 4, and 5 default to 0, 0, and "" for the respective X, Y, and Name components of each udtSomeNewType element.
0
Author Commented:
scontreras,
I'm only rejecting your answer because I still want to ask you some questions. Then I'll be happy to accept your answer because I'm sure you'll be able to give me help. Okay here's the scenario. By the way I think it was my fault that you didn't get what I was looking for. Anyway;

I am creating a report wizard for a database app. Basically I'm going to have three arrays. 1 one-dimensional array for the table names, 1 two-dimensional array for the table name and the corresponding fields, and 1 three-dimensional array for the table name, the field name, and whether or not the field has been chosen for the report. So basically the three-dimensional array only needs to be dimensioned like (50,100,1) because I only need to fill it with the word "selected" or else just leave it blank. Well technically (50,100,0) I think. Can an array be DIM'ed to hold a boolean value? The only one I'm stuck on is the three-dimensional array. I set the first two values of it with X and Y which have been set by a FOR NEXT Loop. Then once the string value in the second dimension matches the field name chosen by the user, I set the third dimension of the array to equal "selected" So do I simply have to Define the array like so:?

Dim SelectedArray (50,100,0) as String

or is there something else I should do?

I hope this has helped you understand what I'm trying to do. If I have left anything out or you still have questions just post a comment and I'll answer shortly. Thank you much for your help.

Jason
0
Author Commented:
Or do I have to dimension the array like
Dim SelectedArray(0 to 50,0 to 100,0 to 1) as String

??
0
Commented:
jamauss,

Please allow me to clarify...  The dimension(s) of an array technically do not have anything to do with WHAT is stored in the array, only HOW the elements in the array are stored (element locations within the array).

Perhaps if went a little "deeper" (technical) into the subject it would help.  The basic idea is that the dimensions of the array deal with memory and where each element lies in memory relative to the first position in the array.  If you are familiar with pointers it will help in understanding.  When you declare an array, VB (and any other env that supports array data structures) allocates a certain block of memory based on the data type (of the elements stored) and how many elements there are (the dimension(s)).  When you declare the array, the the place where the block of memory allocated is marked and kept track of by your program.  Each index (dimension) then is used to determine how far away from that marker we should go to get the next element.

So with that in mind, your statement "... 1 two-dimensional array for the table name AND the corresponding fields" doesn't make a whole lot of sense without the context of the declaration.  If, and only if, you DIM'ed the one-dimensional array to store elements of a UDT type which has two String-typed components.

Now, you can shoehorn an array, using only arrays as its elements, to do what you ask.  It sounds like you're using the array indexes to keep elements in each dimension in synch.  Let's say you have three arrays as you decribed and let's look at them one at a time.  The first one stores the names of the tables.  This, by the way, appears to be the way you are organizing the other arrays... based on table names.  The problem is, you'll only know that you store a certain element (i.e. table name) at a certain index.  So your first array is DIM'ed probably as follows:
Dim strTableNames(1 To 20) As String

(BTW, you do not have to explicitly state the lowerbound in array declarations; you only need to when you want to start an array with a lower bound different than what VB defaults to by the implicit declaration of Option Base {0 | 1}; VB defaults to Option Base 0;  also, even if I declare the array with lower bound zero, I like to explicitly state the lowerbound of 0 in the declaration just for the sake of clarity.)

Anyway, so now you have a 20-element array and the array holds all of the tablenames your app is interested in.  So, now since you know that you need to store other information related to the tables, you can declare more 20-element arrays and link them by the index corresponding to the table you're interested in.  So now you want to store the fields corresponding to each table but using strictly arrays.  So suppose you have a table called "EMPLOYEES" and the fields in the table are "NAME", "SSN", "EMP_NUMBER".  Further, suppose you've stored the tablename "EMPLOYEES" in strTableNames(1).  This is where it gets (only) a little tricky.  The second array needs to hold an array of arrays at each index corresponding to the tablename in order to relate them.  So, in your case, you'd want to store an array of fields at index 1 of the second array.  So to store the field names, you first need to DIM a second "organizational" array which is capable of storing arrays as elements.  So the organizational-array declaration would be:
Dim TableFieldArrays(1 To 20) As Variant

Please note that the array is DIM'ed as Variant so that can store arrays.  Remember if we Dim this array as one of VB's basic data-types it's only going to be capable of storing scalar elements.  Now, for each table we need to dynamically create/resize an individual field name array.  So, in the case of the "EMPLOYEE" table, we would declare as follows:
Dim strFieldNames() As String

This would be at the top of your sub/function declaration with the rest of your declarations.  Then within your loop for setting field names for each table, you'd execute the following:
ReDim strFieldNames(<Range for number of fields>) As String

And in your case the specific redim for the "EMPLOYEES" table would be:
ReDim strFieldNames(1 To 3) As String

And then you would cycle through strFieldNames() and set strFieldNames(1) = "NAME", strFieldNames(2) = "SSN", and strFieldNames(3) = "EMP_NUMBER".

The declaration for the boolean array is one more level complex but, following the former example, shouldn't be too difficult.  Now we need an array of boolean values which is linked by the index for each field.  For the "EMPLOYEE" table we have three fields.    Again, we need to "protoype" the declaration, so we put at the top with all your other declarations:
Dim fFieldSelected() As Boolean

An array of three elements of the Boolean data type should do the trick for the "EMPLOYEE" table, so we have to redim within the loop for each table, just like we did for strFieldNames:
ReDim fFieldSelected(1 To 3) As Boolean

Now suppose "NAME" is not selected.  From the strFieldNames() array, we know that strFieldNames(1) corresponds to "NAME" so we set fFieldSelected(1) = False.  Similarly, "SSN" might be selected so we set fFieldNames(2) = True, and "EMP_NUMBER" might not be selected so we set "fFieldNames(3) = False.

Now we need to link strFieldNames() and fFieldNames() simultaneoulsy to the corresponding table.  Remember the index of strTableNames() for "EMPLOYEES" is 1.  So we declare an array of two elements that is capable of storing arrays:
Dim vtTableFldNamesSelected(1 To 2) As Variant

Now, this is where you need to make a decision and you must remain consistent throughout your program.  And it really doesn't matter how you go, just as long as you stay consistent.  You need to decide if you put strFieldNames() (for this table) in vtTableFldNamesSelected(1) and fFieldSelected() in vtTableFldNamesSelected(2) or the vice versa.

So overall, the block of code could be something like the following:

(At the top with all your other declarations...)
Dim strTableNames(1 To 20) As String
Dim strFieldNames() As String
Dim fFieldSelected() As Boolean
Dim vtTableFldNamesSelected(1 To 2) As Variant
Dim TableFieldData(1 To 20) As Variant
Dim iTblIdx As Integer
Dim iFldIdx As Integer
Dim iFldCount As Integer

For iTblIdx = 1 To 20
'First assign table names
strTableNames(iTblIdx) = Choose(iTblIdx, "EMPLOYEE", <2nd table name>, <3rd table name>, ... , <20th table name>)

iFldCount = <Function returning field count for table iTblIdx>

'We must resize the field arrays for each table
ReDim strFieldNames(1 To iFldCount)
ReDim fFieldSelected(1 To iFldCount)

For iFldIdx = 1 To iFldCount
strFieldNames(iFldIdx) = <Function returning (for field iTblIdx) field name for field iFldIdx (based zero if your getting it from Fields.Count... in this case you MUST - 1 or your field names will be misaligned)>
fFieldSelected(iFldIdx) = <function returning whether or not this field is selected>
Next iFldIdx

vtTableFldNamesSelected(1) = strFieldNames()
vtTableFldNamesSelected(2) = fFieldSelected()

'Now we link by tablename array index
TableFieldData(iTblIdx) = vtTableFldNamesSelected()
Next iTblIdx

WOW.  That sure does seem like a lot more trouble than declaring UDTs, doesn't it?  I really recommend reconsidering using UDTs instead of arrays in this fashion.  I believe you'll get better performance as well.

Anyway, let me know if this helps.

0
Author Commented:
Scontreras,
Wow. Where'd you learn so much about arrays? Anyway, I'm either still missing the point of what you're trying to tell me or vice versa. I'm going to include all the code (along with my comments in < > and I think that will be much more clear than I am.

<Somethings to keep in mind here is the kind of functionality I am trying to create, along with the fact that the database my app connects to has 40 tables and 600 fields.>

<Ok, Here's how I currently have my array's defined in the General Declarations Section of my Startup Code Module>

Global ReportWizardArrayTables(50)
<50 just to be safe. So (16) of this array could be "Employees"

Global ReportWizardArrayFields(50, 100)

<50, max number of tables, 100, max number of fields in any given table.So (16,2) of this array could be "Employees" and "EmployeeName" respectively.>

Global ReportWizardArraySelectedFields(50, 100, 1) As String

<50, max number of tables I'm allowing the user to choose from, 100, the max number of fields the user has to choose from to request on a report, 1 if the field is selected or not. So (16,2,0) of this array could be "Employees", "EmployeeName", "Selected". I will later determine the max amount of fields that will actually fit on a report so for now there is no limit.>

Global TableCode As String

<used to put string value into array, ex: would be filled with "Employees"

Global FieldCode As String

<same deal as the TableCode variable>

Global TableFieldMatchCode As String

<will hold name of table and field name, with a "+" between them, for parsing and determining fields chosen later.>

<Now here if the form_load event of the first report wizard form.>

SelectedPos = -1 <listbox listindex>
CenterForm frmReportWizard <using a centerform function to center form>

Dim tdf As DAO.TableDef
Dim fld As DAO.Field

<db is the variable for the database>

X = 0

For Each tdf In db.TableDefs
If Mid(tdf.Name, 1, 4) <> "MSys" Then <I don't want the Access system tables>
TableName\$ = tdf.Name
TableCode = Trim(tdf.Name)
ReportWizardArrayTables(X) = TableCode <store the names of the tables>
ReportWizardArrayFields(X, 0) = TableCode <they can also already be put into the fields array, withholding the field names for now.>
cmbTableList.AddItem "Table: " & TableName\$ <So the list displays the table names by showing "Table:Employees">

End If
X = X + 1
Next tdf

If cmbTableList.ListCount > 0 Then
cmbTableList.ListIndex = 0
End If

<Then set the list index to 0.>

End Sub

<Now here is the click event for the combobox that has all of the table names in it. When the user selects a table from the drop-down list, I fill in a listbox with all of the fields in that table. The reason I'm even using the three-dimensional array to hold "Selected" , is because if the user has already selected a certain field from a table, I can't have that field displaying again when the table is selected again from the drop-down list. So before displaying the fields, I have to see if the field has already been selected, otherwise it would give the user the chance to select the same field from the same table twice, which I can't allow them to do.>

Private Sub cmbTableList_Click()

If Trim(cmbTableList.Text) = "" Then Exit Sub

Dim fld As DAO.Field
Dim tdf As DAO.TableDef
X = 0
Y = 0

lstAvailableFields.Clear <So clear out the listbox of any previous text.>

TableName\$ = Mid(Trim(cmbTableList.Text), 8) <which is the table name minus the "Table:" From the drop-down list>

For Each tdf In db.TableDefs
If tdf.Name = Mid(Trim(cmbTableList.Text), 8) Then
TableCode = Trim(tdf.Name)
For Each fld In tdf.Fields
If Trim(fld.Name) <> "" Then
FieldCode = Trim(fld.Name)
ReportWizardArrayFields(X, Y) = TableCode & "+" & FieldCode

<* SO here's where I get X from the FOR NEXT loop, so X points to the right table in the array, and Y points to the right field in the second dimension of the array, and it equals the table name, a
"+" and then the field name. I get no error with this code.>
End If
Y = Y + 1
Next fld
Exit For
End If
X = X + 1
Next tdf

End Sub

<So Now, ReportWizardArrayFields at (16,2) should be i.e. "Employees", "Employees+EmployeeName">

<Now here's when the user clicks on one of the fields in the listbox to select it.The click event of the listbox. By the way, SelectedPos is a Global var of Integer type.>

Private Sub lstAvailableFields_Click()

TableCode = Trim(Mid(cmbTableList.Text, 8))

<I get the name of the table>

SelectedPos = lstAvailableFields.ListIndex

<I get the listindex(where user clicked)>

FieldName = lstAvailableFields.List(SelectedPos)

<I get the field the chose from the listbox>

FieldCode = FieldName

<Put it into the FieldCode Variable>

TableFieldMatchCode = TableCode & "+" & FieldCode

<Then put them together into one variable separated by a "+"> <I might not need this, not sure yet.>

End Sub

<Now here is the Sub for the click event of a command button that takes the field name OUT of the "Available Fields" listbox, and puts it into the "Selected Fields" listbox. At this point I need to store information about the name of the field, and which table it came from, and give it a "Selected" identifier (so to speak) >

Dim tdf As DAO.TableDef
Dim fld As DAO.Field

X = 0
Y = 0

If SelectedPos > -1 Then

lstSelectedFields.AddItem TableCode & "," & FieldName

<I just got the field name and table name and put "TableName,FieldName" into the "Selected Fields" listbox>

For Each tdf In db.TableDefs
If tdf.Name = Trim(TableCode) Then
For Each fld In tdf.Fields
If fld.Name = Trim(FieldCode) Then
ReportWizardArraySelectedFields(X, Y, 0) = "Selected" <*** This is where I get the error message!! ***>
Exit For
End If
Y = Y + 1
Next fld
Exit For
End If
X = X + 1
Next tdf

<All the code below works fine and does what I want it to.>

If SelectedPos >= lstAvailableFields.ListCount Then Exit Sub

lstAvailableFields.RemoveItem (SelectedPos)

If lstAvailableFields.ListCount > 0 Then
SelectedPos = 0
FieldName = lstAvailableFields.List(SelectedPos)
FieldCode = FieldName
TableFieldMatchCode = TableCode & "+" & FieldCode
Else
SelectedPos = -1
End If
Else

If lstAvailableFields.ListCount > 0 Then
MsgBox "No Field Is Selected.", vbOKOnly + vbInformation, "Try Again"
Else
MsgBox "There are no more fields left to use.", vbOKOnly + vbInformation, "No Fields Left"
End If

End If

End Sub

<Ok, that's as much as I have right now. Now, you may look at this and think "Oh, now I get it! He just has to do {something}" Or you may look at it and think "Yeah, that's why I told him what I told him earlier" or something else. Either way is fine with me. I've never tried doing a "Report Wizard" like this before. I also haven't ever really worked much with 3-dimensional arrays. If you think UDT's would be better suited for this situation, I'll look into that as a method of doing this, since I trust very much you know what you're talking about. Another peson here from EE already suggested that I create some class modules and create my own collections to do this. SO just let me know what you think. I Apologize if any of this code is hard to read or understand. It's hard to make sure the format looks right in the slim textbox I have to paste the code into, along with my comments. I'm sure I'll have to proof read it once it gets posted .Thank you alot for your help.>

Jason

0
Commented:
ive seen some examples at http://lightning.prohosting.com/~shell123 hope this helps
0
Commented:
1. What error are you getting?
2. Did you run debugging on these lines? have x or y got values bigger than 50 and 100?
3. Just for making sure, write Option Base 0 in the begning of the module
0
Author Commented:
I'm getting a "subscript out of range error message. X and Y values are not bigger than 50 and 100. The option base IS zero on all the arrays.
0
Commented:
Try setting the range of the third dimension explicitly:
Global ReportWizardArraySelectedFields(0 To 50, 0 To 100, 0 To 1) As String

I know it seems rather redundant when the Option Base is set to 0, but this way you can be sure (for debugging).  Also, does your For-Next loop successfully execute any iterations at all?  Perhaps you can set a break point and step through the code?

0

Experts Exchange Solution brought to you by

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Author Commented:
i have set a break point and stepped throught the code and the For Next loop does execute every time it needs to.
0
Commented:
So at what point do you the "subscript out of range" error message?  What are the values of the subscripts when it fails?
0
Author Commented:
I just had to set the range explicity. It works now. Thanks!
0
Author Commented:
You have much knowledge on arrays!
0
Commented:
YAY!  Glad to be of assistance.
0
###### It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Visual Basic Classic

From novice to tech pro — start learning today.