New array in For Next loop every iteration VBS - arrays

I have a section of code which opens each text file in a folder and I want to not only put the file name into an array but also split the text inside the file into an array, something like this:
i = 0
n = 1
For Each File In Folder
i = i + 1
Dim UserArray & i()
Set openedFile = fso.OpenTextFile(File)
Do Until openedFile.AtEndOfStream
Line = openedFile.ReadLine
ReDim Preserve UserArray & i(n)
UserArray & i(n) = Line
n = n + 1
Loop
n = 0
Loop
The idea being that each line will later be strComp to another array of lines from a different text file.
So each file needs to create a unique array name for its text contents and the number of files in any given folder varies.
The above does not work,any ideas?

There are syntax errors in your code(Line 5,9,10 - fixed by using execute statements which allows you to declare variables dynamically during run time with different names) along with variable spelling mistakes(Line 8).
P.S. I am not making any changes to the logic applied here. Just trying to correct the mistakes.
i = 0
n = 0 'initialised to 0
For Each File In Folder
i = i + 1
Execute "Dim UserArray"&i&"()" 'used execute statement to declare arrays with new names based on the value of i for each file
Set openedFile = fso.OpenTextFile(File)
Do Until openedFile.AtEndOfStream
Line = openedFile.ReadLine 'corrected the spelling mistake here
Execute "ReDim Preserve UserArray"&i&"("&n&")"
Execute "UserArray"&i&"("&n&")="&Line
n = n + 1
Loop
n = 0
Loop
After this code, you should have UserArray1 for 1st file, UserArray2 for 2nd file and so on...

You could try an array of arrays. Like so:
Dim Userarray() As Variant
Dim subArray() As String
i = 0
n = 1
For Row = 0 To 4
i = i + 1
ReDim subArray(i)
For Each cell In ActiveSheet.Range(Cells(i, 1), Cells(i, 5))
ReDim Preserve subArray(n)
subArray(n) = cell.Value
n = n + 1
Next
ReDim Preserve Userarray(i)
Userarray(i) = subArray
n = 0
Next
(I didn't have files, so I was just using a range in Excel). I assume VBA is similar enough to VBScript for this to work...Results in the first row of data as an array in Userarray(1), the second row of data as an array in Userarray(2), etc.

Related

Arrays in Excel VBA. At some point it puts NA instead of the value

I am trying to make a simple simulation in Excel VBA in which we roll two dices. What is the probability of getting "1"+"2" or "1"+"3"?
This is my code:
Sub ProbabilityMeyerArray()
Dim i As Long
Dim ArrayDices(1 To 100000, 1 To 2) As Variant
Dim ArrayResult(1 To 100000) As Variant
'Simulation
For i = 1 To 100000
ArrayDices(i, 1) = WorksheetFunction.RandBetween(1, 6)
ArrayDices(i, 2) = WorksheetFunction.RandBetween(1, 6)
If (ArrayDices(i, 1) = 1 And ArrayDices(i, 2) = 3) _
Or (ArrayDices(i, 1) = 1 And ArrayDices(i, 2) = 2) _
Or (ArrayDices(i, 1) = 3 And ArrayDices(i, 2) = 1) _
Or (ArrayDices(i, 1) = 2 And ArrayDices(i, 2) = 1) Then
ArrayResult(i) = 1
Else
ArrayResult(i) = 0
End If
Next i
'print the values to cells
Range("A1:B100000").Value = ArrayDices
Range("C1:C100000").Value = WorksheetFunction.Transpose(ArrayResult)
'Calculate the probability
Probability = Application.WorksheetFunction.Sum(ArrayResult) / 100000
MsgBox "The Probability is " & Probability
End Sub
The problem is that when I print the values from arrays to the cells, then in column C I have 0 and 1 (as it should be), but then from row 34465 I get NA. Here is a screenshot:
https://ibb.co/7jsjjJC
So, for some reason it starts putting NA instead of 0 and 1. The calculation does not work properly either, because the probability is too low, and I guess this is because it only counts the first 34464 zeros and ones, while dividing with 100 000. Can you help me understand what is wrong here? It seems to be a problem with (my understanding of) arrays, since I can run a similar simulation without arrays (by simply using cells), and it works.
Thanks in advance!
As #RaymondWu said in the comments, the problem is that the Transpose function has a limit to the length of the array it can manipulate. This limit is somewhere between 65k and 66k columns.
Indeed, your code will run as expected for 65k iterations.
You can easily avoid using transpose and to be honest I don't see the reason to use it in the first place.
Instead of declaring your array as Dim ArrayResult(1 To 100000) As Variant which by default makes it 1 row x 100000 columns, you can declare it as so:
Dim ArrayResult(1 To 100000, 1 To 1) As Variant
This will make it 100000 rows x 1 columns, which can now be written in a column in excel easily like so:
Range("C1:C100000").Value = ArrayResult
Of course you will also need to change the code accordingly where needed:
ArrayResult(i,1) = 1
Else
ArrayResult(i,1) = 0
A few other tips:
Always use Option Explicit at the very top of the code. It makes the declaration of variables mandatory and it helps to avoid mistakes
Always use explicit references. When referencing Ranges the best practice is to define the worksheet to which they belong e.g. ThisWorkbook.Worksheets("Sheet1").Range("C1:C100000").Value = ArrayResult

How to Pass an Array to and from a Function?

(Fair Warning, I am self taught on VBA so I apologize in advance for any cringe-worthy coding or notations.)
I have an estimating worksheet in excel. The worksheet will have a section for the user to input variables (which will be an array). The first input variable will "reset" the remaining input variables to a standard value when the first variable is changed. The standard values for the input variables are stored in a function in a module. I am attempting to fill the input variable array with the standard values from the function and then display those values on the sheet. I was easily able to do this without arrays but have had no luck in moving everything into arrays.
This is for excel 2010. I previously did not use arrays and created a new variable when needed, however the estimating sheet has grown much larger and it would be better to use arrays at this point. I have googled this question quite a bit, played around with removing and adding parenthesis, changing the type to Variant, trying to set the input variable array to be a variable that is an array (if that makes sense?), and briefly looked into ParamArray but that does not seem applicable here.
Dim BearingDim(1 To 9, 1 To 4, 1 To 8) As Range
Dim arrBearingGeneral(1 To 5, 1 To 8) As Range
Dim Test As Variant
Private Sub Worksheet_Change(ByVal Target As Range)
'Set General Variable array to cells on the worksheet
For i = 1 To 5
For j = 1 To 8
Set arrBearingGeneral(i, j) = Cells(9 + i, 3 + j)
Next j
Next i
'Set Bearing Input Variables to Cells on the Worksheet
For p = 1 To 4
For i = 1 To 9
Select Case p
Case Is = 1
Set BearingDim(i, p, 1) = Cells(16 + i, 4)
Case Is = 2
Set BearingDim(i, p, 1) = Cells(27 + i, 4)
Case Is = 3
Set BearingDim(i, p, 1) = Cells(37 + i, 4)
Case Is = 4
Set BearingDim(i, p, 1) = Cells(49 + i, 4)
End Select
Next i
Next p
'Autopopulate standard input variables based on Bearing Type
inputMD_StdRocker BearingType:=arrBearingGeneral(1, 1), _
arrBearingDim:=BearingDim
End Sub
Sub inputMD_StdRocker(ByVal BearingType As String, ByRef _
arrBearingDim() As Variant)
Dim arrBearingDim(1 To 9, 1 To 4)
Select Case BearingType
Case Is = "MF50-I"
For j = 1 To 2
arrBearingDim(2, j) = 20
arrBearingDim(3, j) = 9
arrBearingDim(4, j) = 1.75
Next j
arrBearingDim(5, 1) = 15
'There are numerous more select case, but those were removed to keep it
'short
End Select
End Sub
The expected output is my "BearingDim" Array will have certain array index values set to a standard value from the "inputMD_StdRocker" function. Then those values will be displayed in the cell that corresponds to the array index.
Currently, I get a compile error "Type Mismatch, Array or User-Defined Type Expected". I have been able to get around the type mismatch by removing the () from "arrBearingDim()" in the function title for "inputMD_StdRocker" however, it will not pass the values back to my "BearingDim" array.
Any help would be greatly appreciated.
This is a partial answer to what (I think) is a misunderstanding you have of how to use arrays. There are a few problems in your code.
First, you're defining a two-dimensional and a three-dimensional array of Ranges when I believe you really only want to store the values captured from the worksheet. (If I'm wrong, then you are never initializing the array of Ranges, so none of the ranges in the array actually point to anything.)
Secondly, it looks as if your initial array arrBearingGeneral is always filled from the same (static) area of the worksheet. If this is so (and you really do want the values from the cells, not an array of Range objects), then you can create a memory-based array (read this website, especially section 19). So the first part of your code can be reduced to
'--- create and populate a memory-based array
Dim bearingDataArea As Range
Dim arrBearingGeneral(1 To 5, 1 To 8) As Variant
Set bearingDataArea = ThisWorkbook.Sheets("Sheet1").Range("D10:K14")
arrBearingGeneral = bearingDataArea.Value
Optionally of course you can calculate the range of your data instead of hard-coding it ("D10:K14"), but this example follows your own example.
While this isn't a complete answer, hopefully it clears up an issue to get you farther down the road.

Displaying some values from an array VBA excel

I'm trying to display multiple values on my excel file based on first line and first column. I've made a 2D array for my values. MyValue is an InputBox variable and I loop over the first column trying to find MyValue and I want to get on display values that are on "PSC", "BCCH" and so on columns, basically the same thing as a filter based on 1st column but displaying only some values from other columns. The output that i have for this operation is in this image:
However 136, 144 and 152 which are PSC values are on line 2, 3, 4 in my source excel file, I'm not sure what are those zeros. I want to diplay those values one under each other, I tried without offset method but if I don't use it I get no value displayed.
Dim1 = wSht.Range("A2", wSht.Range("A1").End(xlDown)).Cells.Count
Dim2 = wSht.Range("A1", wSht.Range("A1").End(xlToRight)).Cells.Count
For i = 1 To Dim1
For j = 1 To Dim2
If wSht.Cells(i, 1) = MyValue Then
If wSht.Cells(1, j) = "PSC" Then
ReDim Preserve Matrice(0 To 5, 0 To Matrice_size)
Matrice(0, Matrice_size) = wSht.Cells(i, j).value
Matrice_size = Matrice_size + 1
End If
If wSht.Cells(1, j) = "BCCH" Then
ReDim Preserve Matrice(0 To 5, 0 To Matrice_size)
Matrice(1, Matrice_size) = wSht.Cells(i, j).value
Matrice_size = Matrice_size + 1
End If
End If
Next j
Next i
For k = LBound(Matrice, 1) To UBound(Matrice, 1)
For l = LBound(Matrice, 2) To UBound(Matrice, 2)
ThisWorkbook.Worksheets(1).Range("A2").Offset(k, l).value = Application.Transpose(Matrice(k, l))
Next l
Next k
switch the first If line with the For j and also the corresponding last End If with the Next j, so the inner loop only runs through the correct rows.
it seems the data starts at line 2, so start the For i loop with 2.
if you only need data from those two columns you can look for them in a separate loop first and save their position, so you don't need a nested loop. (in this case my first point doesn't apply).
you don't even need a loop, you can use Excel's MATCH function.
the matrix and transpose seems to be an overkill. since i determines k and l you can get rid of this part, and write to Worksheets(1) directly from the i-j loop.
you have five rows in the matrix and every fifth output is a correct value, isn't that suspicious?
you seem to put values into line 0 and 1 of the matrix then read from line 1 and 2. this might be the reason for the line full of 0s
if you can provide what exactly you would expect from what exact source data, I can help you more. but without knowing, my solution would look something like
PSC_col = Application.WorksheetFunction.Match("PSC", wSht.Rows(1).EntireRow, 0)
BCCH_col = Application.WorksheetFunction.Match("BCCH", wSht.Rows(1).EntireRow, 0)
For i = 2 To wSht.[A2].End(xlDown).Row
If wSht.Cells(i, 1) = MyValue Then
ThisWorkbook.Worksheets(1).[A2].Offset(k, 0).Value = wSht.Cells(i, PSC_col).Value
ThisWorkbook.Worksheets(1).[B2].Offset(k, 0).Value = wSht.Cells(i, BCCH_col).Value
k = k + 1
End If
Next i

VBA - struggling to calc and write StDev data into an array with a For Next loop

11/24/14 - as per below.....
Still trying to figure this out - might it be easier by creating a smaller array which could roll through the larger array? ...then any necessary calcs could be done on the entirety of the small array.
I cannot figure out how to isolate just a (rolling) subset of an array. The rolling subset could be used for moving averages, standard devs, max/min, etc.
11/21/14 - I have made several attempts, this is the latest iteration. It shouldn't produce meaningful output until the minimum periods have been looped thru (stdev_periods = 10).
--pct_chg_array() is an array which holds percent change data from i=2 to i = 2541... declared as variant
--stdev_periods = 10 ...declared as integer
--i is a counter ...declared as integer
--stdev_array() is an empty array which I hope to populate with a standard deviation calculation for a rolling n period range ...declared as variant
--Option Base 1 and Option Explicit are on
For i = 2 To 2541
If IsNumeric(i) And i <> 0 Then
stdev_array(i, 1) = Application.WorksheetFunction.stdev(Range(pct_chg_array(i, 1).Offset(-stdev_periods, 0), pct_chg_array(i, 0)))
Else
stdev_array(i, 1) = 0
End If
Next i
Any guidance would be immensely appreciated. Thanks!
----EDIT----
Just to simplify, this is how I would express it in a worksheet formula...
=IF(ISNUMBER(OFFSET($E3,-stdev_periods+1,0)),STDEV(OFFSET($E3,0,0,-stdev_periods)),0)
...with "stdev_periods" = 10 and column E holding 1 period %chg data (ie =$E3/$E2-1).
Put this function in the module:
Public Function Slice(vntInputArray As Variant, lngStartIndex As Long, lngEndIndex As Long)
'Use to return an arbitrary-sized subset from a 1 dimensional array.
'Assumes developer is using Option Base 1
Dim vntSubArray() As Variant, lngInputIndex As Long
Dim lngElementCountIndex As Long: lngElementCountIndex = 1
For lngInputIndex = lngStartIndex To lngEndIndex
ReDim Preserve vntSubArray(lngInputIndex)
vntSubArray(lngElementCountIndex) = vntInputArray(lngInputIndex)
lngElementCountIndex = (lngElementCountIndex + 1)
Next lngInputIndex
Slice = vntSubArray
End Function
Adding the function to your code:
For i = 2 To 2541
If IsNumeric(i) And i > stdev_periods Then 'Using greater than to account for Option Base 1
stdev_array(i, 1) = WorksheetFunction.stdev(Slice( pct_chg_array, (i -stdev_periods), i))
Else
stdev_array(i, 1) = 0
End If
Next i

Sheet/Range Array to loop through making graphs

Apologies aside, my problem has become quite the curiousity and I can't find any answers as of yet.
A piece of my program is designed to search through a graph to see if a series already exists, and if it doesn't, it creates it. As of now the program is fine and dandy. HOWEVER, in doing so for 8 different graphs, the code is not only long, but could potentially be more inefficient. So, I decided to attempt to loop through an array. This is what one of the graphing blocks looks like:
EDIT: I didn't specifically say, but Z IS the name of a sheet AND the name of the series, as defined earlier in the code, so that is not an issue.
Sheets("A").Select
Count = ActiveChart.SeriesCollection.Count
Fail = 0
For c = 1 To Count
If ActiveChart.SeriesCollection(c).Name = Z Then
With ActiveChart.SeriesCollection(c)
.Values = Worksheets(Z).Range("AJ5:AJ45")
.XValues = Worksheets(Z).Range("AP5:AP45")
End With
Exit For
Fail = Fail - 1
End If
Fail = Fail + 1
Next c
If Fail = Count Then
ActiveChart.SeriesCollection.NewSeries
c = ActiveChart.SeriesCollection.Count
With ActiveChart.SeriesCollection(c)
.Values = Worksheets(Z).Range("AJ5:AJ45")
.XValues = Worksheets(Z).Range("AP5:AP45")
.Name = Z
.MarkerStyle = 1
.MarkerSize = 9
End With
End If
(With 7 more of those underneath, with different sheets and Y values). SO, I tried this:
Dim SheetArr(0 To 7) As Sheets
Set SheetArr(0) = Sheets("A")
Set SheetArr(1) = Sheets("B")
etc..
Dim RangeArr(0 To 7) As Range
Set RangeArr(0) = Range("AJ5:AJ45")
Set RangeArr(1) = Range("AK5:AK45")
etc..
And starting a loop, replacing individual values with SheetArr(i) and RangeArr(i) respectively so only one block of code exists, but none of the graphs even begin plotting.
Am I dimming arrays wrong, or is this just not possible?
(Sorry for a super long post, just trying to be as clear as possible.)
Instead of trying to create an array of sheets, which I'm not sure you can do, you can create an array of strings containing the sheet names.
Dim SheetNames(0 To 7) As String
SheetNames(0) = "A"
SheetNames(1) = "B"
...
Then use it like this
For IntSheet = 0 To 7
ThisWorkbook.Sheets(SheetNames(IntSheet)).Select
...
Next
Address Comment About Ranges:
I stepped through the code below, testing out the range assignment and the appropriate ranges were selected
Dim RangeArr(0 To 7) As Range
Set RangeArr(0) = Range("AJ5:AJ45")
Set RangeArr(1) = Range("AK5:AK45")
RangeArr(0).Select 'Selects AJ5:AJ45
RangeArr(1).Select 'Selects AK5:AK45

Resources