Variant array passed from UserForm to Module in VBA is null/empty - arrays

I have a UserForm with a ListBox for the user to select values. Those values are populated in UserForm_Initialize() via a function call to the base module, which returns an array as variant. This works without problems.
If the user selects some values and presses a button, the buttons Click event calls another function in the base module to pass on the user-entered array and compute things. This does not work at all. The value received in the base module is always nonexistent (not even null, but I don't know the correct VBA term, nothing is there at all).
Things I have tried so far:
Passing all arguments ByVal: Did not make a difference
Using global shared variables: This did work, but I don't want to rely on them if all I do is pass a single array to a single function. This also introduces state into the code which has to be managed, especially when reusing the function
Accessing the functions by full qualifiers: Did not make a difference. The functions are found and executed correctly, but the argument variables are empty, therefore the functions fail later on when doing the calculations.
My question is: How can I pass arrays from UserForms to Modules (not vice versa) without relying on global variables and without losing the array content?
This question may be related to this question about passing a String from Form to Module, but the accepted answer does not help in my case (using global variables).

When adding the code as requested in the comments, I stumbled upon that fact that I could print the content of the array, but it would not show anything in the debugger and the size would be 0.
The size issue was because I used Len(array) instead of Application.CountA(array) and I had a leftover On error resume next from earlier still in the code, which meant that no error was raised and size was always set to zero... This was the reason for the strange behaviour.

Related

Adding a new Entry in a Struct holding a TArray as Member value doesn’t update it’s entries

I am currently working on a Character Customization System where a HUDLayout dynamically create Widgets based on a set of Skins available for the Character Selected. A Skin is represented as a Struct called MaterialInstanceContainer and holds a TArray. Player can mix and match their selection according to the Body Parts they select. In order to achieve the final result, I want to create a TMap<string, MaterialInstanceContainer> so that I can map each BodyParts available for selection with the individual material instance targeting the same BodyPart.
ISSUE: My issue is as follow, when I try to foreach over my collection of Material Instances inside my Container, I do a string comparison and if the output is valid, I can then break my struct to access the Material Instance Array and ADD to it however, at the very end of the process, the length of the array inside Material Container is still at zero.
How can I add a new entry in the array that my Material Container Struct hold?
Thanks!
enter image description here
The issue here is actually pretty straight forward: in Blueprints when you 'Find' a member of Map you are not getting it by-reference, instead you get the copy.
This is exactly what happens at the end of your nested loop: You get a copy, you add item to it, and when another iteration kicks-in the copy gets terminated.
And here on my side it returns exactly the same result as expected:
The fix for that would be easy:
After editing a Copy, you can overwrite the Map member by its copy (via 'Add' node).
But in your case it will be more tricky. You cannot just plug the same BreakStruct/Array node that you just used because it would call whole Find sequence again which creates another copy. Look
If you are confused. This code actually looks like this for Unreal's point of view.
So you have to store the the Struct in local variable first, then perform any operations on it and after everything is done - overwrite the Map member by its locally stored copy. And the result is
Map member gets overwritten every time and everything is as it should be.
Well, almost... For some reason your code is stored in Macro. I think you have to change it to a Function to be able to create local struct variable. But it shouldn't be a problem in this specific case because in your code there is no logic that needs to be done in macro.
Hope, it helps

How do I get my Julia code to assign the scalar value of a variable to an array without changing it as they variable mutates later on?

For my simulation, I have a field that is called particle.current_theta. When this field is a single variable, I assign it a new value that is called just "theta" on my line 177. This theta has its value changed further down within my code, on lines 202 and 206. I want what I have printed in my terminal as tree_theta and current_theta to be very similar to each other but not quite identical (This part of the code basically detects whether or not my particle is entering or exiting a region). You can see all this in the image below:
Now, I need to make the field I have called particle.current_theta a [1x1] array, and assign the entry in my [1x1] array the "theta" value, as usual. However, simply making particle.current_theta a [1x1] array radically changes its value within the terminal and causes my simulation to break. You can see how the value for particle.theta (printed in terminal as "current theta") is now drastically different in the code below:
I suspect that making particle.currenttheta an array is making it mutate whenever theta is changed in some of those lines below. How do I prevent that from happening, and get results that are identical to using just a single variable. To be precise, I want particle.current_theta to save the numerical information that theta has at line 177 of code but not be changed afterwards. Because of the large size of my code now and the function calls within function calls, it would be infeasible for me to be able to create a mwe that replicates this issue. However, all help and advice is appreciated, and I will respond to and clarify any questions that people may have.
If theta is a scalar (and it appears to be), then it's unlikely that changing its value is what is changing particle.currenttheta. What is more likely is that you're passing the currenttheta to some function, and changing the value of the passed argument inside the function. Julia arrays are passed to functions "by reference", in the sense that a copy is not made, and instead any changes made inside the function change the original array. When you had currenttheta as a scalar (which are passed "by value" instead), when you pass that to a function, changes made inside the function do not affect the original currenttheta's value.
So if you're passing currenttheta to a function somewhere and don't want it to be modified inside the function, pass copy(particle.currenttheta) in that call instead.
If you're not doing that, or that doesn't solve the problem, we need more of the code to figure out where the change might actually be happening. If not the whole of it, at least the parts that handle currenttheta, and the parts that print it. (Also, it would massively help with clarity if you would use consistent names in the output. Sometimes it's treetheta and current theta, other times it's theta and particle.currentheta, and it's not clear where these are being printed from and what the difference - if any - is.)

Indirect and Concatenate #ref error on *open* workbook

I've been trying to find the answer to this, and the only results I'm getting are #ref errors on closed workbooks. I'm getting this error when the workbook I am referencing is open.
So here's what I'm doing - I'm trying to blend numbers daily from different banks from multiple worksheets with multiple tabs each. Since those worksheets have a date followed by a random ID number, we're going to just rename them to a set name (eg, "pmac") and dump each into a folder. Then, we'd open each one we want to blend, and I'll use "ISERROR" to turn any reference errors to blank so only the open workbooks are included in the blend (which I'll try to add as soon as I can get this indirect reference to work).
The team that would be using this is on a shared drive, so I decided to make some dynamic fields "RatesheetFolder" and "PennyMacFileName", so if we ever needed to move or rename stuff, we'd change it in one place instead of updating every formula.
So currently, my formula looks like this:
=INDIRECT(CONCATENATE("='"&RatesheetFolder&"["&PennyMacFileName&"]Conventional'!B"&ROW(B17)))
but it returns a #REF error. Everything I'm reading is saying that it's because the workbook is closed... but it isn't. It's open. If I copy and paste the value from the formula above into a cell, then THAT reference works... so the concatenation wasn't done incorrectly either, but for some reason it won't reference the open workbook.
I also tried doing the same thing, but on a local directory, in case the shared drive was an issue... same problem though. (for reference, here is the pasted value of the above formula)
='C:\Users\username\Documents\Reports\In Progress\[pmac.xlsx]Conventional'!B17
I also tried leaving out the folder path entirely, since I'd be dealing with open workbooks, and that also didn't work (copy/pasting values of the concatenated string still works, so again, it's not that it's written incorrectly). Also tried having both files in the same folder, no luck.
Any idea what's going on? Is it some kind of security thing that my company might be blocking the indirect reference or something? Maybe the way I'm using the row function?
I am using Office 2016.
Additional:
So, boiling things down to their absolute minimum....
=$B$2 returns the value in B2
=CONCATENATE("="&ADDRESS(2,2)) returns the text "=$B$2"
=INDIRECT("=$B$2") returns a #ref error
=INDIRECT("$B$2") returns the value in B2 (so only without the "=")
=INDIRECT(CONCATENATE("="&ADDRESS(2,2))) returns a ref error
=INDIRECT(CONCATENATE(ADDRESS(2,2))) returns the value in B2
Thinking it was the equals sign causing the trouble, I tried this:
=[pmac.xlsx]Conventional!B17 which returned the value from the other workbook
=INDIRECT([pmac.xlsx]Conventional!B17) still returns a ref error...
so I'm lost again.
I tried the last two things again, but with the full directory path (not just filename) and same thing - actually writing the direct reference out worked
='C:\Users\username\Documents\Reports\In Progress\[pmac.xlsx]Conventional'!B17
but the indirect reference didn't
=INDIRECT('C:\Users\username\Documents\Reports\In Progress\[pmac.xlsx]Conventional'!B17
Oddly - when I did the Indirect formula with the full path and hit enter it removed the full path and changed it back to just the filename (since the workbook was open):
=INDIRECT([pmac.xlsx]Conventional!B17)
but it still threw up a reference error. So it recognizes the workbook and that it's open... but it still gives an error trying to reference it. The indirect-concatenate does seem to work when I'm not referencing another workbook, so it does seem that's where the issue is... but I don't know why.
So I guess I found the answer after messing around with quotation marks and stuff in both the indirect formula and the concatenate formula.
To start, I realized that INDIRECT(B2) gave a ref error, while INDIRECT("B2") worked. So I went back through and tested a few things out making sure concatenate returned the exact right thing, and then surrounded that with an indirect formula.
So when I did =INDIRECT([pmac.xlsx]Conventional!B17) this didn't work because it wasn't a string. If I did =INDIRECT("[pmac.xlsx]Conventional!B17"), THEN it worked. Indirect requires it to be a string.
Looks like the problem was that Concatenate returned [pmac.xlsx]Conventional!B17 as text - but when I copy/pasted the VALUE that the concatenate formula returned, that then reverted the string back to a value, and stopped working inside the indirect formula.
What ended up working (and I swear I tried this already....) was:
=INDIRECT(CONCATENATE("'"&RatesheetFolder&"["&PennyMacFileName&"]Conventional'!B"&ROW($A17)))
I must have accidentally removed the single quote mark before RatesheetFolder when I removed the equal sign causing it to fail, and then every attempt after that was me trying to break it down into the individual parts, and unknowingly undoing the "string" property that concatenate applied whenever I pasted the result it into the indirect formula.
TL;DR of what I figured out
The Indirect formula requires it's contents to all be text/string
The results of Concatenate are returned as text/string, making additional quotation marks around those results redundant and cause an error
If you're referencing a cell directly, you need the equal sign
=B2, but if you're doing an indirect reference, you want to leave
the equals sign out of the indirect formula =INDIRECT("B2")
So putting that all together (in this example DynamicVar's value is simply the letter B) :
=INDIRECT("B2") works
=INDIRECT(B2) doesn't work
=CONCATENATE(DynamicVar & 2) returns B2 without quotations, but it's a string, so..
=INDIRECT(CONCATENATE(DynamicVar & 2)) works

How to assert an actual value with two different expected values

I'm writing a protractor test which asserts a URL against two different browser URLs.
I tried using toMatch with a regular expression but I get an error.
Is it possible to assert an actual string value against 2 or more expected string values and see if it is equal to any of them?
expect(url1).toMatch(/this.url|www.google.com/);
Sounds like you want to use toContain() which works for finding an item in an array. So switch your assertion around because you want to pass it an array of URLs [this.url, 'www.google.com'] and assert that it will contain url1.
expect([this.url, 'www.google.com']).toContain(url1);
Note: While this should work, personally I don't like to have my values on the receiving end of the assertion i.e. toContain(url1), I would rather that be passed first expect(url1).... However, in the end it's still asserting actual vs expected values so not a huge deal in my opinion.
https://jasmine.github.io/2.0/introduction.html

Sorting and managing numerous variables

My project has classes which, unavoidably, contain hundreds upon hundreds of variables that I'm always having to keep straight. For example, I'm always having to keep track of specific kinds of variables for a recurring set of "items" that occur inside of a class, where placing those variables between multiple classes would cause a lot of confusion.
How do I better sort my variables to keep from going crazy, especially when it comes time to save my data?
Am I missing something? Actionscript is an Object Oriented language, so you might have hundreds of variables, but unless you've somehow treated it like a grab bag and dumped it all in one place, everything should be to hand. Without knowing what all you're keeping track of, it's hard to give concrete advice, but here's an example from a current project I'm working on, which is a platform for building pre-employment assessments.
The basic unit is a Question. A Question has a stem, text that can go in the status bar, a collection of answers, and a collection of measures of things we're tracking about what the user does in that particular type of questions.
The measures are, again, their own type of object, and come in two "flavors": one that is used to track a time limit and one that isn't. The measure has a name (so we know where to write back to the database) and a value (which tells us what). Timed ones also have a property for the time limit.
When we need to time the question, we hand that measure to yet another object that counts the time down and a separate object that displays the time (if appropriate for the situation). The answers, known as distractors, have a label and a value that they can impart to the appropriate measure based on the user selection. For example, if a user selects "d", its value, "4" is transferred to the measure that stores the user's selection.
Once the user submits his answer, we loop through all the measures for the question and send those to the database. If those were not treated as a collection (in this case, a Vector), we'd have to know exactly what specific measures are being stored for each question and each question would have a very different structure that we'd have to dig through. So if looping through collections is your issue, I think you should revisit that idea. It saves a lot of code and is FAR more efficient than "var1", "var2", "var3."
If the part you think is unweildy is the type checking you have to do because literally anything could be in there, then Vector could be a good solution for you as long as you're using at least Flash Player 10.
So, in summary:
When you have a lot of related properties, write a Class that keeps all of those related bits and pieces together (like my Question).
When objects have 0-n "things" that are all of the same or very similar, use a collection of some sort, such as an Array or Vector, to allow you to iterate through them as a group and perform the same operation on each (for example, each Question is part of a larger grouping that allows each question to be presented in turn, and each question has a collection of distractors and another of measures.
These two concepts, used together, should help keep your information tidy and organized.
While I'm certain there are numerous ways of keeping arrays straight, I have found a method that works well for me. Best of all, it collapses large amounts of information into a handful of arrays that I can parse to an XML file or other storage method. I call this method my "indexed array system".
There are actually multiple ways to do this: creating a handful of 1-dimensional arrays, or creating 2-dimensional (or higher) array(s). Both work equally well, so choose the one that works best for your code. I'm only going to show the 1-dimensional method here. Those of you who are familiar with arrays can probably figure out how to rewrite this to use higher dimensional arrays.
I use Actionscript 3, but this approach should work with almost any programming or scripting language.
In this example, I'm trying to keep various "properties" of different "activities" straight. In this case, we'll say these properties are Level, High Score, and Play Count. We'll call the activities Pinball, Word Search, Maze, and Memory.
This method involves creating multiple arrays, one for each property, and creating constants that hold the integer "key" used for each activity.
We'll start by creating the constants, as integers. Constants work for this, because we never change them after compile. The value we put into each constant is the index the corresponding data will always be stored at in the arrays.
const pinball:int = 0;
const wordsearch:int = 1;
const maze:int = 2;
const memory:int = 3;
Now, we create the arrays. Remember, arrays start counting from zero. Since we want to be able to modify the values, this should be a regular variable.
Note, I am constructing the array to be the specific length we need, with the default value for the desired data type in each slot. I've used all integers here, but you can use just about any data type you need.
var highscore:Array = [0, 0, 0, 0];
var level:Array = [0, 0, 0, 0];
var playcount:Array = [0, 0, 0, 0];
So, we have a consistent "address" for each property, and we only had to create four constants, and three arrays, instead of 12 variables.
Now we need to create the functions to read and write to the arrays using this system. This is where the real beauty of the system comes in. Be sure this function is written in public scope if you want to read/write the arrays from outside this class.
To create the function that gets data from the arrays, we need two arguments: the name of the activity and the name of the property. We also want to set up this function to return a value of any type.
GOTCHA WARNING: In Actionscript 3, this won't work in static classes or functions, as it relies on the "this" keyword.
public function fetchData(act:String, prop:String):*
{
var r:*;
r = this[prop][this[act]];
return r;
}
That queer bit of code, r = this[prop][this[act]], simply uses the provided strings "act" and "prop" as the names of the constant and array, and sets the resulting value to r. Thus, if you feed the function the parameters ("maze", "highscore"), that code will essentially act like r = highscore[2] (remember, this[act] returns the integer value assigned to it.)
The writing method works essentially the same way, except we need one additional argument, the data to be written. This argument needs to be able to accept any
GOTCHA WARNING: One significant drawback to this system with strict typing languages is that you must remember the data type for the array you're writing to. The compiler cannot catch these type errors, so your program will simply throw a fatal error if it tries to write the wrong value type.
One clever way around this is to create different functions for different data types, so passing the wrong data type in an argument will trigger a compile-time error.
public function writeData(act:String, prop:String, val:*):void
{
this[prop][this[act]] = val;
}
Now, we just have one additional problem. What happens if we pass an activity or property name that doesn't exist? To protect against this, we just need one more function.
This function will validate a provided constant or variable key by attempting to access it, and catching the resulting fatal error, returning false instead. If the key is valid, it will return true.
function validateName(ID:String):Boolean
{
var checkthis:*
var r:Boolean = true;
try
{
checkthis = this[ID];
}
catch (error:ReferenceError)
{
r = false;
}
return r;
}
Now, we just need to adjust our other two functions to take advantage of this. We'll wrap the function's code inside an if statement.
If one of the keys is invalid, the function will do nothing - it will fail silently. To get around this, just put a trace (a.k.a. print) statement or a non-fatal error in the else construct.
public function fetchData(act:String, prop:String):*
{
var r:*;
if(validateName(act) && validateName(prop))
{
r = this[prop][this[act]];
return r;
}
}
public function writeData(act:String, prop:String, val:*):void
{
if(validateName(act) && validateName(prop))
{
this[prop][this[act]] = val;
}
}
Now, to use these functions, you simply need to use one line of code each. For the example, we'll say we have a text object in the GUI that shows the high score, called txtHighScore. I've omitted the necessary typecasting for the sake of the example.
//Get the high score.
txtHighScore.text = fetchData("maze", "highscore");
//Write the new high score.
writeData("maze", "highscore", txtHighScore.text);
I hope ya'll will find this tutorial useful in sorting and managing your variables.
(Afternote: You can probably do something similar with dictionaries or databases, but I prefer the flexibility with this method.)

Resources