A bit new to vba and I had a buddy gave me this line:
ReDim Preserve InputD(1 To UBound(InputD, 1), 1 To 3)
I'm not fully sure what this line is doing, I understand the ReDim to change the values in the array and the preserve which keeps all data except the last. What exactly is the "To" statements doing inside this array? is it just specifying how many times to redim?
ReDim Preserve acts different then ReDim. It is able to modify only the last dimension and preserve the (existing) values (if any). The way you use it is a little strange. I mean, normally it is good to ReDim an array to dimensions (especially the last one) able to cover the estimated number of elements to be loaded, plus something over. Usually, a variable being incremented with each new element added shell be used, in order to Redim Preserve at this specific variable number.
For instance:
Sub testRedimPreserve()
Dim sh As Worksheet, arr, arrToProcess, i As Long, k As Long
Set sh = ActiveSheet
arr = sh.Range("A2:A20"): k = 1
ReDim arrToProcess(1 To 2, 1 To UBound(arr)) 'redim at a maximum which surely will not be exceeded
For i = 1 To UBound(arr)
If arr(i, 1) = "x" Then
arrToProcess(1, k) = "Row " & i: arrToProcess(2, k) = "OK": k = k + 1
End If
Next i
'Since some values in the range are not "x", the obtained array must use Redim Preserve:
ReDim Preserve arrToProcess(1 To 2, 1 To k - 1) 'All the elements value up to k -1 are kept/preserved
'Since only the last dimension can be preserved, the obtained array must be trasnposed.
sh.Range("C2").Resize(k - 1, 2).Value = WorksheetFunction.Transpose(arrToProcess)
End Sub
1.a Of course, if you have a bigger array and you need only its first three columns, you can proceed like your friend suggested.
Redim arr(1 to x) means that the first element of the dimension is 1. In such a case it will keep elements from 1 to x. If not specify in this way, I mean ReDim arr(x), the first element is zero. This is the default VBA. It works in 0 base. Except the case when Option Base 1 is used on top of the module...
If you look at the documentation for ReDim you will see subscripts in the parenthesis and if you look further down you will see an explanation of subscripts:
Required. Dimensions of an array variable; up to 60 multiple dimensions may be declared. The subscripts argument uses the following syntax:
[ lowerTo ] upper [ , [ lowerTo ] upper ] . . .
When not explicitly stated in lower, the lower bound of an array is controlled by the Option Base statement. The lower bound is zero if no Option Base statement is present.
What this means is that you resize your array InputID from whatever it was before to (from 1 to its upper bounds from before) for the first dimension and (from 1 to 3) for the second dimension.
Related
I have a "Datevarie" dynamic matrix which contains many dates.
At some point in the routine I have to extract from DateVarie the lowest value.
The problem is that when I use the Lbound function, I always return the value 1 instead of the date (for example "01/01/2018").
I think the data in the array is entered correctly as I debugged it step by step several times.
How can I resolve without having to report data on a sheet and order them? Do I have to use FOR ... NEXT? The dates can be 2-3 but also several tens
Type interventi
...
data as date
...
public Intervento as interventi
public rs1 as adodb.recordset
...
Sub TheSubWithProblem(...)
...
dim Datevarie() as date
..
redim preserve etc..
DateVarie(k)=format(RS1!DataI,"dd/mm/yyyy") '<<<< correct
...
Intervento.data=lbound(datevarie) '<<<< always return 1
...
The problem is that when I use the Lbound function, I always return the value 1 instead of the date (for example "01/01/2018").
LBOUND and UBOUND functions return the lower- and upper bounds of the array, not the lowest and highest values within the array.
Arrays in VBA are base-0 by default, so an initialized array should typically have LBound of 0.
If you want to get the lowest or highest value, then you need to implement your own algorithm to do so (e.g., a for/next loop), or if this is a single-dimensional array we can potentially make use of an ArrayList object which has (among other things) a Sort method:
Set d = CreateObject("System.Collections.ArrayList")
Dim dateVarie As Variant
Dim i As Long, m As Long, day As Long
For i = 1 To 100
m = Application.WorksheetFunction.RandBetween(1, 12)
day = Application.WorksheetFunction.RandBetween(1, 30)
d.Add (DateValue(DateSerial(2018, m, day)))
Next
'Preserve your un-sorted array, if needed:
ReDim dateVarie(d.Count - 1)
dateVarie = d.ToArray()
Msgbox "The first value is: " & dateVarie(0)
d.Sort ' Sorts the arraylist:
MsgBox "The lowest value is: " & (d(0))
Arrays start at 0. This is why the below returns 0:
Sub TestMe()
Dim datevarie(2) As Date
Debug.Print LBound(datevarie)
End Sub
And it does not matter, whether you put values in the array or not.
If you want an array to start at 1, in VBA write Option Base 1 and make sure that noone sees the code, because you may have problems with your colleagues/peers:
Option Explicit
Option Base 1
Sub TestMe()
Dim datevarie(2) As Date
Debug.Print LBound(datevarie)
End Sub
I got the total number of columns, and tried to append it to string in such a way it is 1,2,3,4,5 depending on the number of columns.
Sub DeleteHighlightedRecords()
numofCol = ""
For i = 1 To lcol
numofCol = numofCol & "," & i
Next i
numofCol = Mid(numofCol, 2)
ThisWorkbook.Worksheets(1).Cells.RemoveDuplicates Columns:=Array(numofCol), Header:=xlNo
End Sub
The above code i have will match the combination i placed in Array(), and then remove the duplicates ya? Correct me if i am wrong, however I am getting application-defined error.
How may i set the dynamic numbers of columns into the Array()?
Array(1,2,3) is not the same as Array("1,2,3"). The first will produce an 3-element array containing 1,2 and 3, the second will produce an 1-element array containing the string "1,2,3".
To create an actual array we can do the following:
Dim numOfCol() As Variant 'normally I would choose Long instead of variant but that gave a type mismatch error
Dim i As Long 'always use "Option Explicit" at the top of your module!
ReDim numOfCol(0 To (lcol - 1)) 'has to be 0-based for RemoveDuplicates
For i = 0 To (lcol - 1)
numOfCol(i) = i + 1
Next i
Now as if using a Variant array instead of a Long array and using a 0-based array wasn't enough, RemoveDuplicates apparently also can't handle arguments passed as a reference to a variable instead of a value. To pass a variable as a value rather than a reference, encase it in parentheses. This causes the variable to be evaluated and then passed to the function.
ThisWorkbook.Worksheets(1).Cells.RemoveDuplicates Columns:=(numOfCol), Header:=xlNo
I keep on getting an error when I try and pass this value to an array, essentially I am working with exchange rates, I have the currency in the second dimension of the array ary() and I am looking to retrieve a data point in a table that is 2 cells away from the element stored in the array.
Now I believe the problem has something to do with the value being a number and the array being a variant, however I cannot solve the problem.
Option Explicit
Dim fxary(), y
ws.Select
Set Rng = Sheets("BEXR").Range("C2:C" & Sheets("BEXR").Range("A1").End(xlDown).Row)
For i = 1 To UBound(ary, 2)
If ary(2, i) <> ary(2, UBound(ary, 2)) Then
For Each y In Rng
If Sheets("BEXR").Range(y.Address).Value = ary(2, UBound(ary, 2)) And Sheets("BEXR").Range(y.Address).Offset(0, 1).Value = ary(2, i) Then
fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value ' error occurring here
fxary = ary(1, i)
End If
Next y
End If
Next
Your comments say that I have omitted all the lines not related to this specific problem, therefore I will post an answer based on the assumption that all the lines that are related to the problem have been included in the code in the question.
The following comments should be made about your code:
You have used Option Explicit but nowhere do you declare the size or type of the variables i and ary.
Nowhere do you specify values for the variable ary.
Your line saying fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value is trying to assign a scalar to a vector, and will generate a Type Mismatch error.
Your line saying fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value is immediately followed by a line (fxary = ary(1, i)) replacing the value of fxary with something else, so the first line is redundant.
All your references to Sheets("BEXR").Range(y.Address) can be replaced with y.
You are executing through two loops (from 1 to whatever the second dimension of the undefined variable ary is and, within that loop, through every cell in column C of your sheet), but you are effectively only setting fxary once, i.e. to the value in the undefined variable ary where the first dimension's value is 1 and the second dimension's value is i. (But I haven't been able to wrap my mind around the code enough to be able to work out which value of i that will end up being - I think it's worked out by looking at each row of your sheet that has a different value in column C than in column D, and the value in column C is equal to ary(2, highest value), and then determining the highest value of i such that ary(2, i) will equal the value in column D.)
I believe you need to look at each of the above items before your code will do anything meaningful, but to resolve the specific problem you say you are having:
1) Change your declaration of fxary to
Dim fxary, y
2) Simply remove the line saying
fxary() = Sheets("BEXR").Range(y.Address).Offset(0, 2).Value
as that line is redundant (as I said in one of my earlier points).
For a project of mine in VBA, I need to have a custom type like this:
Public Type Gene
Name As String
Expression() As Double 'holds data points, is a vector
Norm_Expression() As Double 'Holds normalized data, is a vector
Min_Corr As Double
Min_Profile As Integer
End Type
But I'm finding it difficult to change the size of the the inner arrays by a redim statement.
ReDim Data(n)
For i = 1 To n
ReDim Preserve Data.Expression(Max_Time_Point)
Next i
Does not work. Neither does:
For i = 1 To n
ReDim Data(n).Expression(Max_Time_Point)Does not work. Neither does:
Next i
Could you help? Thank you!
Almost there - you need to indicate which member of Data you want to work on:
For i = 1 To n
ReDim Preserve Data(i).Expression(Max_Time_Point)
Next i
Note when you do this
ReDim Data(n)
it's the same as ReDim Data(0 to n) so in your loop if you start at 1 you miss the first element.
I have a loop wherein I take the mean of several columns of numbers with the same number of rows each.
The point of the loop is to capture these means in a new vector.
So for each loop I need to indicate "all rows". In matlab this would be easy, just use ":" But I can't figure out what the analogy is in VB. Please help! Thanks.
(Please advise me as to what I put in the code below where I have ALLROWS).
My attempt so far:
For i = 1 To CA
mrCA11(i) = Application.WorksheetFunction.Average(revCA11(**ALLROWS**,i))
Next i
In matlab this would be:
For i = 1:CA
mrCA11(i) = mean(revCA11(:,i));
Next i
EDIT: I've also tried this trick to no avail:
For j = 1 To CA
For i = 1 To s11
temp11(i) = revCA11(i, j)
Next i
mrCA11(j) = Application.WorksheetFunction.Average(temp11)
Next j
I get the error message: "Unable to get the Average property of the Worksheet Function class"
As everybody (Tim and shahkalpesh at least) pointed out, we need to understand what is revCall or more specifically, we need to understand how you want to give them ALL ROWS in argument.
Finding the last row (or column or cell)
A common Excel issue is to find the last used row / column / cell.
This will give you the end of your vector.
Excel give you several methods to deal with this:
xlTypeLastCell
Last cell used in the entire sheet (regardless if it's used in column A or not)
lastRow = ActiveSheet.Cells.SpecialCells(xlCellTypeLastCell).Row
End(xlUp)
Last cell used (including blanks in-between) in Column A is as simple as this:
lastRow = Range("A" & Rows.Count).End(xlUp).Row
End(xlToLeft)
Last cell used (including blanks in-between) in Row 1 is as simple as this:
lastRow = ActiveSheet.Cells(1, Columns.Count).End(xlToLeft).Row
UsedRange
Last cell used in the WorkSheet (according to Excel interpretation):
Set rangeLastCell = ActiveSheet.UsedRange
Using an array as argument
The methods above told you how to find the last row (if this is what you need). You can then easily create your vector and use it in your procedure revCA11.
You can either give an array as argument as Tim pointed out in his answer with this kind of statement:
myArray = ActiveSheet.Range("A1", Cells(lastRow, lastColumn).Value
Or you can use the integer (or long) to build your vector inside your procedure as simple as declaring a range:
Range("A1:A" & lastRow)
You might clarify exactly how revCA11 is declared/created, but maybe something along these lines might work for you:
Sub Tester()
Dim arr, x
arr = ActiveSheet.Range("A1:D5").Value '2-D array
'average each column
Debug.Print "Columns:"
For x = 1 To UBound(arr, 2)
Debug.Print x, Application.Average(Application.Index(arr, 0, x))
Next x
'average each row
Debug.Print "Rows:"
For x = 1 To UBound(arr, 1)
Debug.Print x, Application.Average(Application.Index(arr, x, 0))
Next x
End Sub