VBA dynamic array assigns only one dimension - arrays

I have some strange behavior with a dynamic array. It seems like VBA denies assigning a value to a variable. Here is the code:
Private Sub Report_Load()
Dim db As Database
Dim reportArray() As Variant
Dim structre As Recordset
Dim columns, rows, i As Integer
'Open recordset
Set db = CurrentDb
Set structure = db.OpenRecordset("SELECT * FROM [tblstructure] ORDER BY [Rank]")
'Change array size dynamically
rows = structure.RecordCount
columns = 4
ReDim reportArray(rows, columns)
'Populate array
i = 0
Do Until structure.EOF
reportArray(i, 0) = structure![Name]
i = i + 1
structure.MoveNext
Loop
End Sub
When I open the report I get an error that subscript is out of range. When I debug my code I can see that value of i in the loop is 2, so my array must be smaller that that. When I hover over rows variable I see that its value is 1. So indeed I'm trying to access something that is out of range. But the strange part is that the value of structure.RecordCount is 23.
A screenshot:
Even if I use the code like this:
ReDim reportArray(structure.RecordCount, columns)
I get an array of size (1, 4). Why isn't VBA assigning value 23 to variable "rows" or why ReDim assigns the right value to second dimension, but not the first?

Read this: https://support.microsoft.com/en-us/kb/105976
CAUSE
For recordsets and snapshots, Microsoft Access does not
automatically return the number of records that exist in the
recordset. Rather, it returns the number of records accessed.
RESOLUTION
To determine the exact number of records in a recordset or snapshot,
use the MoveLast method before checking the RecordCount property.
STATUS
This behavior is by design.
As per microsoft , RecordCount returns the number of records accessed and not the number of total records.
So, in your case, this should work
structure.MoveLast
rows = structure.RecordCount
structure.MoveFirst

It's been a while since I've used Access, but I seem to recall that RecordCount doesn't work properly unless you've actually enumerated the entire resultset doing something like structure.MoveLast first (just remember to do structure.MoveFirst before looping through). Of course, that requires a recordset capable of moving in both directions.
Alternatively, you could put the ReDim in your loop, and just increase it by one each time.

Related

Type mismatch error when creating a vba array

I'm trying to drop a range of cells straight into an array to run some calculations. The range will always be A8:E?, but I never know how many total rows there will be. One of the columns contains string values that I need for determining conditions. I set a variable to find the last row (and no, this data never has any blanks so the method to set it works just fine). This is what I have"
Sub use_arr()
Dim rw As Integer
rw = Worksheets("Sheet1").Range("A8").End(xlDown).Row
Dim myArr()
myArr = Worksheets("Sheet1").Range("A8:E" & rw)
End Sub
But it keeps throwing a "Run_time error '13'" at me.
Arrays are still new to me, someone suggested using them instead of looping through cells for faster execution. So I'm still figuring this out.
Either declare myArr just as a Variant, not an array, or specify the Value property of the range:
myArr = Worksheets("Sheet1").Range("A8:E" & rw).Value

Assigning values of a range to an array

I have write bellow code, that prints 210 in immediate window.
Sub RangeToArr()
Dim data() As Variant
data = Range("Salary[EmpNum]").Value
Debug.Print UBound(data)
End Sub
When I using bellow, the Subscript out of range Run-time error message will displayed.
Sub RangeToArr()
Dim data() As Variant
data = Range("Salary[EmpNum]").Value
Debug.Print data(210)
End Sub
If data is an array, what is the problem, else, how can access a range values that assigned to an array, as above?
A range converted to an array will create a 2 dimensional array, if you try to return data(210) you are referencing it as if it's a single dimensional array. You can get the value from the 210th cell in the range by returning
data(210, 1)
I can only assume as to the reason why it forces a 2 dimensional array. My guess is because if your range included additional columns it would be 2 dimensional array. The act of creating a 2 dimensional array regardless even if you only have one column means the method for dealing with the data remains the same. There is an exception to this - If your range is a single cell, VBA will populate just the value as a single dimension.
To make sure your sample works, make sure you have 210 rows in your table sample.
In general, try something like this to get the idea of the arrays and ranges:
Crate a simple named range MyNamedRange with more than one column and more than one row
Put some values in it
Run this and see what you are getting in the immediate window:
Sub RangeToArr()
Dim arrVar As Variant
arrVar = Range("MyNamedRange")
Debug.Print arrVar(1, 2)
End Sub
Change the (1,2) values a bit to get the idea.

Possible to Run Goal Seek on array elements within VBA, instead of an Excel sheet range?

Possible to Run Goal Seek on array elements within VBA, instead of an Excel sheet range?
I have an array that takes initial values from an excel input sheet, does many calculations, and prints values back into a report region on an excel sheet; the output is roughly 200 rows x 28 columns of calculated values. I used to do this with formulas in every cell, but it was very, very slow. I converted to an all-vba Sub that does the calculations quickly and pastes the finished values into the report range in excel.
The problem is that I also need to run Goal Seek on various cells and Excel can't do it on a cell that just has a value, it needs a cell with a formula. With my fancy, efficient array, I can't goal seek anymore!!!!!
Is there a way to run some version of Goal Seek NOT on excel sheet ranges but on array members, like on MyArray(107,23) by testing an input value that is actually on the excel sheet, like Range("B2")? What would that code look like?
The first subroutine uses Range while the second uses Array instead. The goal here is 0.
First subroutine :
Sub GoalSeekWithRange()
Dim i As Long
For i = 1 To 10
Range("C" & i).GoalSeek Goal:=0, ChangingCell:=Range("A" & i)
Next
End Sub
Second subroutine :
Sub GoalSeekWithArray()
Dim arGoal As Variant
Dim arChanging As Variant
With ThisWorkbook.Sheets("MySheet")
Set arGoal = .Range("C1:C10")
Set arChanging = .Range("A1:A10")
End With
Dim i As Long
For i = 1 To 10
arGoal(i, 1).GoalSeek Goal:=0, ChangingCell:=arChanging(i, 1)
Next
End Sub
To avoid headaches, remember an array of range has two dimensions
Probably, you will have to adapt these codes to fit your specific need

Creating an Excel VBA array with unassigned variables as entries

I am trying to set up a workflow/UX where a CSV file is imported and each column of the imported file is assigned a "data type" using dropdowns at the top of each column. Once these data types are designated/assigned to each column, another macro populates a second sheet with the imported CSV data, where the location in the new sheet is dependent on the data type designated for each column of the imported data.
For example, if the first column of the imported data is of data type "DataA", the dropdown selection would be selected as such for this first column (from a total of 12 "data types" in the dropdown menu). This "DataA" data would then be populated in the second sheet in its fifth column.
Here is the code I have so far:
Dim DataA As Integer, DataB As Integer, DataC As Integer, DataD As Integer, DataE As Integer, DataF As Integer, DataG As Integer, DataH As Integer, DataI As Integer, DataJ As Integer, DataK As Integer, DataL As Integer
Dim ColArray(12) As Variant
For p = 1 To LastColImport 'This is a previously-defined/assigned variable
q = 1
Do While q <= 12
If ActiveSheet.DropDowns(p).Value = q Then
ColArray(q) = p
Exit Do
Else
q = q + 1
End If
Loop
Next p
This populates the ColArray array with an integer entry if the data type is selected, or an empty entry if it has not been selected. The next step I want to do is assign each ColArray entry value to a named variable, so that I can call the ColArray entry values by data type name instead of having to remember or look up what data type each ColArray integer value refers to.
I can't find a built-in "dropdown list range name" recall function anywhere, so what I would like to do is the following:
Dim ColArrayNames(12) As Variant
ColArrayNames(1) = DataA 'These variables were defined in the previous code block
ColArrayNames(2) = DataB
...
ColArrayNames(12) = DataL
ColArrayNames = ColArray
I realize that in this specific case, it would probably be easier to just assign the data type variables directly to the ColArray values, instead of putting them into an array and then equating the array values. I feel like populating an array with unassigned variables could be useful in other cases as well. My attempts at using this method of assigning variables have failed.
After changing the last line of code to:
For i = 1 to 12
ColArrayNames(i) = ColArray(i)
Next i
The ColArray values don't get assigned to the data type variables. That being said, the ColArrayNames entries are assigned the correct values, so the issue seems to be the "last step" in assigning the ColArray values to the data type variables by way of the ColArrayNames array of unassigned variables.
If anyone has suggestions for how to approach this "general" problem of using arrays of unassigned variables to assign values to each array entry (while preserving the ability to call these values using the entries' "original" variable names), or if there's a more efficient way of approaching this spreadsheet function altogether, please let me know!
EDIT 1: As requested by John Coleman, I'll elaborate a bit more on what I'm trying to do here.
Once I have the imported column numbers assigned to a data type, I want to send the data to a second sheet with some code in a manner such as:
For i = 2 to LastRow 'The LastRow variable value will be found using a simple xlDown search process
Worksheets(2).Cells(1,i).Value = Worksheets(1).Cells(DataA,i).Value
Worksheets(2).Cells(4,i).Value = Worksheets(1).Cells(DataB,i).Value
Etc.
Next i
Again, I realize that I could just as easily use
Worksheets(2).Cells(1,i).Value = Worksheets(1).Cells(ColArrayNames(1),i).Value
and so on, but I feel like if what I'm asking about is possible, I might be able to use it in another situation (even if it's not the most ideal method for this example).
For as best as I understand your problem, it seems to come down to effectively (and efficiently) defining a data map between the original source data and the formatted output (in this case, cells on a second worksheet).
When confronted with the data typing part of the problem, I realized the ultimate "holder" of the destination data type is the object that holds it -- in this case its the worksheet cell, not the VBA variable or array. Your focus seems to be on the VBA code that transfers/copies the data from one worksheet to another (or to/from variant arrays). Your own choice in efficiency should be your guide here. If you want to use relatively general purpose code for multiple data types, my suggestion is to use a Variant value or array and after copied to the destination cell, set the format of the destination cell (which effectively "types" it for whatever later use necessary).
A dictionary may not even be necessary if the mapping table can be flexibly scanned to accommodate any number of data types, formats, or columns.

SpecialCells(xlCellTypeVisible) not returning all values to array (Excel 2013)

I have a range with 170000 rows in it. I'm filtering column A for a single value and returning the corresponding values in column B.
I want these values to dump into an array so I can quickly toss them into a dictionary (with the key being the value I filtered column A with).
The problem is that SpecialCells(xlCellTypeVisible) is acting inconsistent.
If I do the same test on a smaller range, the values dump into the array just fine. But with a range as large as mine, it only returns the first value in the range. Also, I can use the same line to copy to another sheet. I just can't get it to populate the array.
foo = ws1.Range(tbl1Name & "[ID]").SpecialCells(xlCellTypeVisible)
Works with small ranges, but returns only the first result in a range as large as mine (less than 50 results.) foo becomes an array containing all the variables.
ws1.Range(tbl1Name & "[ID]").SpecialCells(xlCellTypeVisible).Copy ws2.Range("A1")
Works with the large range and copies all the relevant data successfully.
So my question: How do I populate the array without the extra step of copying to a blank worksheet when autofiltering a large table range? (Excel 2013)
EDIT: requires a reference to "Microsoft Forms 2.0 Object Library" (should be near the top of the list of available references). Or add a userform to your project and that will auto-add the reference (you can then remove the form...)
This should work for a single column:
Sub Tester()
Dim rng, txt As String, cb As New DataObject, arr
Set rng = ActiveSheet.Range("A2:A28").SpecialCells(xlCellTypeVisible)
rng.Copy
DoEvents
cb.GetFromClipboard
txt = cb.GetText
arr = Split(txt, vbCrLf)
Debug.Print LBound(arr), UBound(arr)
End Sub
If you had multiple columns you'd need to loop over each element of arr (splitting its value on tab) and transfer the values to a 2-d array.

Resources