For-looping VBA vectors - arrays

I'm trying to create a vector in VBA using a for loop. My problem is that VBA doesn't allow me to have a different equation for the first vector coordinate. When trying to run it I get "expected array" as an error message
'radius calculations
r(1) = (al * Log(al) / (al - 1)) * rb ' middle radius of block 1, trying to calculate first entry
'in r-vector, since this equation is different from the rest
r_m(1) = rb 'r_i-1/2 i=1
For i = 2 To n_r
r(i) = al * r(i - 1) ' r_i
r_m(i) = (r(i) - r(i - 1)) / (Log(r(i) / r(i - 1)))
Next i
al and rb is defined as Double (Public Const), while r is defined as a string. I have only used Matlab in the past, and only read intro guides to VBA (VBA for Dummies etc.)
Greatly appreciate all help in the matter
E

VBA doesn't treat strings the same way it treats arrays (like you'd see in other languages). r(1) looks like array syntax to VBA (as can be seen here); therefore, it's going to error out if it's declared as a string.
So, essentially, VBA is interpreting r(1) to mean, "I have an array named r and I want to store something into element number 1". But, instead, it's attempting to do this to an immutable string.
The Split function can be used to turn a string into an array, if that's what you need.
Still, it'll be best if you straight-up declare an array for your vector math.
Dim myArray() As Double
Dim myArray2(10, 10, 10) As Double
The ReDim keyword can be used to resize an array, even within a for loop. (Just don't forget to ReDim Preserve if you need to make sure that the contents don't get wiped during this operation).

Related

Is there a way to use a 2-dimensional array as the Y-argument in Excel's FORECAST functions?

Excel's FORECAST functions take a 1-dimensional array for both the 'known Xs' argument and the 'known Ys' argument, and then returns a single value as the answer.
I'd like to use a 2-dimensional array for the 'known Ys' argument, and return an array (1-dimensional) as the answer.
In other words, I want to return a set (array) of forecasts that correspond to a set (array) of Y-values, covering the same time-scale (X-values).
(There's a reason I need this...I need to multiply the result I get by a couple of other arrays.)
But if I take a formula that works fine, like
FORECAST.LINEAR($H$1,A2:G2,$A$1:$G$1)
and then change the 1-dimensional array to 2-dimensional (G7 instead of G2):
FORECAST.LINEAR($H$1,A2:G7,$A$1:$G$1)
and press Ctrl+Alt+Enter, I get an error (#N/A).
Same with the TREND function.
I know some Excel functions don't like taking an array as an argument--though sometimes there are ways around this (like here: Can Excel's INDEX function return array?). But I can't figure out if it's possible to 'dereference' things in my situation...I certainly haven't managed to incorporate this approach here.
Addendum in response to comment: The data below are representative (though the real data have a lot more rows and columns!). The top row represents the 'known X's (this is a time scale) and the subsequent rows are the data. The result I want to end up with is an array representing the forecasted Y-value corresponding to X=8...here, I believe that would be
11.71; 14.43; 177.71; 25.71; 16.71; 10.86;
So here is an attempt, I'm still not sure what you are after, but let me try.
I've went the UDF way and my assumption is that you feed the function with a range that has as many rows as columns.
Function GetResult(RNG1 As Range, RNG2 As Range) As Double
Dim X As Double, RNG3 As Range, ARR() As Variant
ReDim ARR(RNG2.Rows.Count - 1)
For X = 1 To RNG2.Rows.Count
Set RNG3 = Application.Intersect(RNG2, Rows(X + 1))
ARR(X - 1) = Application.WorksheetFunction.Forecast(RNG2.Columns.Count + 1, RNG3, RNG1)
Next X
GetResult = Application.WorksheetFunction.Forecast(RNG2.Columns.Count + 1, ARR, RNG1.Value)
End Function
On the sample data you give (with the extraction of column 7), it would look like this:
The function will create a forecast for each line and stores it in an 1-dimensional array. Then it will use all these forecasts to create a final forecast.
Is this close to what you expect?

ReDim existing array with a second dimension?

I declared an array in my VBA function that has a dynamic size. As I cannot ReDim the first dimension of a two- or more-dimensional array, can I add a second dimension to a set array?
This is how I dynamically set the size of my array.
Dim nameArray() As String
Dim arrayCount As Long
For i = 1 To 100
ReDim Preserve nameArray(1 to arrayCount)
nameArray(arrayCount) = "Hello World"
arrayCount = arrayCount + 1
Next i
Now I would like to add a second dimension.
ReDim Preserve nameArray(1 To arrayCount, 1 To 5)
doesn't work.
Is there a workaround?
There isn't any built-in way to do this. Just create a new two-dimensional array and transfer the contents of your existing one-dimensional array into the first row of that new array.
This is what this function does:
Function AddDimension(arr() As String, newLBound As Long, NewUBound As Long) As String()
Dim i As Long
Dim arrOut() As String
ReDim arrOut(LBound(arr) To UBound(arr), newLBound To NewUBound)
For i = LBound(arr) To UBound(arr)
arrOut(i, newLBound) = arr(i)
Next i
AddDimension = arrOut
End Function
Example usage:
nameArray = AddDimension(nameArray, 1, 5)
There is one (also works to delete a dimension), but you will have to think in terms of worksheet dimensions...
use transposition
While I highly prefer the previous 'by hand' method from Jean-François Corbett's and I don't like to rely on Excel build-in function (especially this one!), I would just like to clarify another way for future readers coming here:
adding a dimension to a 1d line vector (a row) means transposing it in Excel
Here, nameArray(1 to arrayCount) is a row (index is a column number) and because of it, if you add a dimension it will become a column since 2d arrays are indexed as (row,column). So, you can just do this:
nameArray = Application.Worksheetfunction.Transpose(nameArray) 'transforms the array to nameArray(1 To arrayCount, 1 To 1), so then:
redim preserve nameArray(1 To arrayCount, 1 To 5)
without any other manipulation.
BUT beware of the very confusing Excel's Transpose function (at least it is the case for me!): the advantage here is that it automatically adds a dimension and redimensions the array for you.
It works as expected only because you are using a 'based 1' index '1d array'.
IF this is not the case, all indices will be shifted by 1, (that's how Transpose is build in to be coherent with cells and ranges). That is: if you start with
nameArray(0 to arrayCount)
you will end up with
nameArray(1 to arrayCount + 1, 1 to 5)
with precautions
While I am at it, it may be off-topic and their are many topics about it, but their are other traps in this Transpose function one should never be enough warned about (not to mention it can consume more time and resources than Jean-François Corbett's solution) :
• if you are dealing with '1d column' in an excel 2d array (a column), that is an array:
nameArray(1 to arrayCount, 1 to 1) [*]
and if you make the transpose of it, the column dimension will be "skipped" and the result will be:
nameArray(1 to arrayCount)
It makes sense since you will end up with an Excel row (so why bother with an extra dim?). But I have to say this is not the intuitive behaviour I would expect, which should be more something like nameArray(1 to 1, 1 to arrayCount).
Note that, a contrario, it can be used to delete a dimension and redimension the array automatically from 2d to 1d: redim preserve the last dimension to 1 to 1 and then transpose! This is the resulting array just above.
• But finally all is not lost: suppose you transpose this array of 1 line:
nameArray(0 to 0, 0 to arrayCount)
you correctly get an array of 1 column:
nameArray(1 to arrayCount + 1, 1 to 1)
(well, almost) - and look back at ref [*] now...
Thus, if you are to use this build-in function and if, moreover you need more dimensions or worst, need to compose transpositions, it can become a bit tricky...
For all this or if you simply need to know correct indexing and number of dimensions of your arrays (not only number of columns), I would suggest a very useful function from users John Coleman and Vegard, the post just below.
While all this should appear logical and trivial for people used to work with Excel sheets, it's very not the case when you are more used to matrix manipulations and I think suggesting the use of this Transpose function should come with some precisions.

Assigning values to a dynamically resized array in Excel VBA

I am trying to populate a two dimensional array of ranges. I don't know how big the array will need to be, so I am using the ReDim and Preserve functions to dynamically re-size the array as required.
I am encountering runtime error 91: "Object variable or With block variable not set" when I run the code.
I am not an experienced coder, but I have managed to isolate the error, and am sure it is coming from the pseudo code below.
Can anyone see any mistakes I have made that would produce the runtime error?
Dim ArrayName() as Range
Dim counter as Integer
If condition = True Then
counter = counter + 1
ReDim Preserve ArrayName(0, counter - 1)
ArrayName(0, counter - 1) = Cells(counter, counter) 'I get a runtime error here
End If
Thank you.
If you want to store ranges within your array you need to add Set before problem line in this way:
Set ArrayName(0, counter - 1) = Cells(counter, counter)
But if you want to store values of the cells you need to change declaration line to this:
Dim ArrayName() as Double 'or String or Variant depending on value type you need to keep in array

EXCEL VBA Error: "Compile Error: Expected Array"

Can anyone help me?
I have been getting a compile error (...: "Expected Array") when dealing with arrays in my Excel workbook.
Basically, I have one 'mother' array (2D, Variant type) and four 'baby' arrays (1D, Double type). The called subroutine creates the publicly declared arrays which my main macro ends up using for display purposes. Unfortunately, the final of the baby arrays craps out (giving the "Compile Error: Expected Array"). Strangely, if I remove this final baby array ('final' - as in the order of declaration/definition) the 2nd to last baby array starts crapping out.
Here is my code:
Public Mother_Array() as Variant, BabyOne_Array(), BabyTwo_Array(), BabyThree_Array(), BabyFour_Array() as Double 'declare may other variables and arrays, too
Sub MainMacro()
'do stuff
Call SunRaySubRoutine(x, y)
'do stuff
Range("blah") = BabyOne_Array: Range("blahblah") = BabyTwo_Array
Range("blahbloh" = BabyThree_Array: Range("blahblue") = BabyFour_Array
End Sub
Sub SunRaySubRoutine(x,y)
n = x * Sheets("ABC").Range("A1").Value + 1
ReDim Mother_Array(18, n) as Variant, BabyOne_Array(n), BabyTwo_Array(n) as Double
ReDim BabyThree_Array(n), BabyFour_Array(n) as Double
'do stuff
For i = 0 to n
BabyOne_Array(i) = Mother_Array(0,i)
BabyTwo_Array(i) = Mother_Array(2,i)
BabyThree_Array(i) = Mother_Array(4,i)
BabyFour_Array(i) = Mother_Array(6,i)
Next
End Sub
I have tried to declare all arrays as the Variant type, but to no avail. I have tried to give BabyFour_Array() a different name, but to no avail.
What's really strange is that even if I comment out the part which makes the BabyFour_Array(), the array still has zero values for each element.
What's also a bit strange is that the first baby array never craps out (although, the 2nd one crapped out once (one time out of maybe 30).
BANDAID: As a temporary fix, I just publicly declared a fifth dummy array (which doesn't get filled or Re-Dimensioned). This fifth array has no actual use besides tricking the system out of having the "Compile Error: Expected Array".
Does anyone know what's causing this "Compile Error: Expected Array" problem with Excel VBA?
Thanks,
Elias
In your global declarations you are only declaring the last baby array as Double. You're declaring the first three as arrays of Variants. But in the subroutine you are Redimming babies one and three as Variants, and two and four as Doubles.
See this Chip Pearson page and scroll down to "Pay Attention To Variables Declared With One Dim Statement."
In VBA when you declare something like:
Dim x, y, z as Long
only z is a Long, the rest are Variants. The correct form is:
Dim x as Long, Y as Long, Z as Long (or Double arrays in your case.)
You can see how this would cause the behavior you describe, i.e., the last one errors, but the others don't.

VBA arrays and Operators

I am trying to write a couple lines of code in VBA for excel, here is what the VB.Net version of the code looks like.
ThisVariable <<= 8
Variable.Add(ThisVariable)
How do I write these lines of code in VBA? VBA does not have the "<<=" operator and does not have the .Add property of an array. Any help would be greatly appreciated.
For the first question, VBA doesn't have built in bit shifting, but you can add a function that does it for you. Try this one.
For the second question, to increase an array size, you have to use the ReDim command. Here's the info on that.
Also, you could use a collection instead of an array. With a collection, you can add & delete item within it at will.
Good luck
<<= left-shifts the number by 8 binary digits, i.e. multiplies the number by 256. For adding something to an array you can re-dim the array:
ThisVariable = ThisVariable * 256
Dim U As Long
Dim L As Long
L = LBound (Variable)
U = UBound (Variable)
ReDim Preserve Variable (L To U+1)
Variable (U+1) = ThisVariable
Note that redim'ing the array just to add an element is not very efficient. You should try to find another approach (e.g. use a larger array and store the number of "valid" elements in a counter variable -- or even create a class module for that).

Resources