I have a UDF that returns a row vector. I would like to use the results of the said UDF in another function. Thus I would like to use a variable to store the array results to be used as input in second function. When I want to recall an element of the variable, I would get an error "Object required".
u = test()
x = u.Item(4)
What is missing in my code? Thanks
You need to reference the index of the array without the Item property (this does not exist for arrays) like so:
u = test()
x = u(4)
As the other posts mention - you would not use the .Item property for an array in VBA. As such, you would simply do:
u = test()
x = u(4)
As for your question regarding why you need to transpose the function, but here's some guidance:
VBA doesn't really handle one-dimensional arrays super well. As such, depending on what you're trying to do with it, you often have to transpose the array into a single "row". I believe that it's basically the equivalent of changing.
x = u(n)
into
x = u(1,n)
Related
VBA question. I'm using it to make Solidworks macros, but that's not important.
Can someone explain the syntax to pass an array (1-Dimensional of type Double, with length of three) to a subroutine or function so I can modify the contents. I know that they have to be passed by reference. I'm talking exact syntax, because everything I try I get type mismatch error on the subroutine/function call.
All I'm looking for is the correct Dim statement for the array of Doubles, initialize statement to make all array values zero, then the subroutine call statement and subroutine header that I would need. Please be specific if I need to use variant type or dynamic array, even when I already know the type and size of the array. Use a function or a sub, I don't care which.
My code works fine as of now, but I'm tired of avoiding function and subroutine calls when I'm using arrays. I've looked at countless documentation and similar questions on here, but I just cannot figure it out. Thanks a lot.
This is fairly trivial, using the ByRef keyword in the function signature will pass your array by reference rather than by value. This means that manipulations to that array will be preserved and bubble up to the calling scope. This is probably an oversimplification, but think of it this way:
ByRef: you're passing a reference to the thing (array, Object, etc.) and any transformations to that thing will be apparent anywhere that thing exists.
ByVal: you're passing the value representation of the thing. Essentially, you're passing a "copy" of it. You can manipulate this object, but it is not the same object from the calling scope, it's just a copy. So when the enclosing scope ends, you're still left with only the original thing.
Initialize your array as a numeric type and that will give you default 0 values.
Example as follows:
Option Explicit
Sub foo()
Dim myArray(1 To 3) As Double 'The array is initialized with zero-values
Call bar(myArray)
MsgBox myArray(3)
End Sub
Sub bar(ByRef dblArray() As Double)
dblArray(1) = 123
dblArray(2) = 345
dblArray(3) = 33456
End Sub
By default, arrays are passed by reference. I've seen in a few places that passing "ByVal" throws an exception. However, I have been able to use ByVal without issue in O365.
I'd like to assign an array of integer values to a corresponding property of an array of objects.
class MyClass {
var propint: Int = 0
var propdbl: Double = 0.0
var propstr: String = ""
} // MyClass
var arrint: [Int]()
var arrobj: [MyClass]()
One approach would be looping through the array of integers (arrint), and assign the value to the property propint of the corresponding object array element.
I'm wondering if there's a more efficient, elegant solution to this undertaking, eg. like array.map, and if so how this would be looking like.
--Sil68
I'm not sure about efficient. As #Ryan said all the built in functional methods just use a loop to process the array.
Having said that, you could do something like this...
let updatedObjectArray = zip(arrint, arrobj).map {
number, object -> MyClass in
object.propint = number
return object
}
You must be careful here though. By using a class (and not a struct) you will inadvertently be editing the original array.
Anyway, this will zip up both arrays, iterate them and then map them onto an array with the updated objects.
As a side note: choose better names for your variables. Even in Stack Overflow questions. Using variable names like arrint or arrobj or propint only do two things. Make it hard to reply to questions because auto correct wants to change them all. And make it hard to understand when reading. I know it's a property and an int (thats what the code says). The name should tell me WHY it's a property and an int.
Not sure if its possible, and I can't find a specific question that shows how to do this. I am basically looking to write a function that would work like the SUM (or any number of functions in Excel). The user can call the function from the spreadsheet and select a variably sized, one dimensional set of numbers from the SS as an argument. For illustrative purposes I've tried writing the following test function to learn how to do this:
Function Test(tester() As Double) As Double
MsgBox "array position 1 = " + tester(1)
Test = tester(1)
End Function
This is just one manifestation of the things I've tried to get this to work, but anytime I choose data from the SS as an argument to my function, it makes the cell go "#Value". I'm thinking I need to do something clever with ranges but am very new to this. It is worth noting that I can pass arrays around function behind the scenes in code, but can't master allowing the user to select the data from the SS. Any help is appreciated!
Try something like this
Public Function TestFunc(TestRange As Range) As Variant
Dim vArray As Variant
vArray = TestRange.Value2
If IsArray(vArray) Then
TestFunc = vArray(1, 1)
Else
TestFunc = vArray
End If
End Function
When I rebuild the solution I get the warning:
"Variable 'aMyArray' is used before it has been assigned a value."
A function in VB.NET uses an array which is dynamically populated.
Example:
Function MyArray()
Try
Dim aMyArray()
For i = 0 to 100
ReDim Preserve aMyArray(i)
Next
Catch ex As Exception
End Try
End Function
How should I declare a dynamically populated array to eliminate this warning?
I like to use the CreateInstance to avoid possible errors:
Dim aMyArray() as string = Array.CreateInstance(GetType(String),0)
A. Your function returns nothing, so you should also have a warning about that
B. You seriously need to turn on Option Strict and let the compiler point out other errors like aMyArray has no Type.
C. Never use and empty Try/Catch; if there is an exception, you want to know when something goes wrong (and where) so you can fix it.
D. Avoid arrays like the plague. ReDim Preserve aMyArray(i) creates a new array and then copies all the data to the new structure. This can be costly in terms of performance if it is a large array of something like strings. Net has several very nice collections such as List(of T) which do not need to be explicitly resized:
Private myList As New List(of String) ' or Integer, Decimal or even MyClassObject
...
myList.Add("Hello")
myList.Add("Lists...")
myList.Add("Goodbye")
myList.Add("Arrays")
D(1). The result of using a List would mean that entire procedure can be done away with. Just add new things to the list as needed.
E. The code posted cant actually result in the warning because you dont ever add a value to it. Adding: aMyArray(2) = 2 after the loop will result in the warning. This is because you have never fully declared the array (size and type) as in:
Dim aMyArray(100) As String
ReDim simply resizes the array which isnt the same thing.
F. Since there is nothing in your new array, there is no reason to use ReDim Preserve because there is nothing to preserve. I'll assume that is supposed to be some calculation. On the other hand, if you are trying to resize an existing array, it should be a Sub.
We are all left to guess if it really is meant to modify an existing array (based on ReDim Preserve) or return a new array of stuff (considering the array is declared in the procedure).
Again, none of this is needed using a List, Dictionary or other Collection Type.
I am playing around with OOP in MATLAB, and I have the following constructor:
function obj = Squadron(num_fighters, num_targets, time_steps)
if nargin == 0
num_targets = 100;
time_steps = 100;
num_fighters = 10;
end
obj.num_shooters = num_fighters;
for iShooter = 1:obj.num_shooters
a(iShooter) = Shooter(num_targets, time_steps);
end
obj.ShooterArray = a;
obj.current_detections = zeros(num_fighters, num_targets);
end
That temporary variable 'a' smells terrible. Is there a better way to initialize an array of objects, I wish there was a push/pop method. I am sure there is a better way to do this.
Looks like you are trying to create an array of handle objects (Shooters) and store it inside the property of another handle object (a Squardron). I have had a very similar problem discussion that might help you.
In short: What you are doing might not be pretty - but might be pretty good already.
When creating an array in Matlab it is usually a good Idea to do some pre-allocation to reserve memory which speeds up performance significantly.
In a normal case something like this:
a=zeros(1,1000);
for n=1:1000
a(n)=n;
end
(here a=1:1000; would be even better)
For objects the pre-allocation works by assigning one of the objects to the very last field in the array. Matlab then fills the other fields before that with objects (handles) that it creates by calling the constructor of that object with no arguments (see Matlab help). Hence a pre-allocation for objects could look like this:
a(1,1000)=ObjectConstructor();
for n=1:1000
a(n)=ObjectConstructor();
end
or simply
for n=1000:-1:1
a(n)=ObjectConstructor();
end
Making sure Shooter can be called with no arguments you should be able to do something like:
for iShooter = obj.num_shooters:-1:1
obj.ShooterArray(iShooter) = Shooter(num_targets, time_steps);
end
However, it turns out that for some reason this direct storing of an array of objects in another object's property creates very bad performance. (Probably the array pre-allocation does not work well in this case). Hence using an auxiliary variable and allocating the full array at once to the property is in this case is a good idea to increase performance.
I would try:
for iShooter = obj.num_shooters:-1:1
a(iShooter) = Shooter(num_targets, time_steps);
end
obj.ShooterArray = a;
Again - for more detail see this discussion
There are a couple of ways to handle this situation...
Building object arrays in the constructor:
You could modify your Shooter class such that when you pass arrays of values it creates an array of objects. Then you could initialize ShooterArray like so:
obj.ShooterArray = Shooter(repmat(num_targets,1,num_fighters),...
repmat(time_steps,1,num_fighters));
Replicating instances of a value class:
If Shooter is a value class, and each object is going to be exactly the same (i.e. you don't initialize any of its default properties to random values), then you can create just one object and replicate it using REPMAT:
obj.ShooterArray = repmat(Shooter(num_targets,time_steps),1,num_fighters);
Unfortunately, if Shooter is a subclass of the handle class, you can't just replicate it as you can with a value class. You would actually be replicating references to just one object, when you really need a number of separate objects each with their own unique reference. In such a case, your current code is likely the best solution.