I have a problem. And I hear the first step is admitting you have a problem. So you are my witnesses I am admitting I have a problem.
So just what is this problem? pcSoft has turned me into an Array Junkie. It started off like all addictions do, I tried a few normal arrays (the gateway drug), then that wasn’t enough so I started using structured arrays, and now that I am a full blown junkie, I am using associative structured arrays (mainlining!), the dashboard class for TeamAlogy.com has over 200 arrays in it, and its getting to the point where when I run into an issue, my first thought is how could an array solve this. Yes I have a problem, but it sure is a good problem to have!
This week I am going to share my addiction with you. We will start with the basic of arrays and move into associate arrays, structured arrays, and even get into associative structured arrays.Once you see all that arrays can offer, I think it won’t be long before you to will have an array addiction. If you have looked at the File Manager Classes by Andy Stapleton and don’t fully understand them this this weeks Uncle Pete’s corner is for you, when you peel back all the window dressing of the class, you find that the foundation of the File Manager Classes is a Structured Array. So once we have gone through a primer on structured arrays, we will look at how those pieces fit into and are used my the File Manger classes, we will also look at a few other real world challenges I have faced that arrays helped solved.
So read on and let’s get started.
So let’s start with the basics, just what is an array? An array is variable that has multiple instances (elements). OK, so if you don’t know what an array is that probably doesn’t help you much. But if we break it done it gives us a few basic rules of arrays. All of the instances have to be the same type, but since that type can be a structure or even a class, that is not as limiting as you might think. The number of instances can be fixed or dynamic. And a few terms that are used with arrays:
- Element – A single instanced of a the array.
- Subscript – The number used to directly reference a specific Element of the array.
- Dimension – Arrays can be multi-dimensional, and we will cover that a little later
- Fixed or Dynamic – Is the number of Elements allowed set when it is define or can more be added and deleted as needed?
- Simple Array – An array based on a simple variable type (int, string, etc)
- Structured Array – An array based on a complex variable type (structure, class, etc)
- Associative Array – An “indexed” array that is accessed by an index value instead of a subscript.
The first 5 items on that list are the basics that most programming languages support, but WX adds a few cherries on top with the last two.
So let’s look at some basic code. Let’s say we are working on a Sales system and we need to display the totals sales for each month of the year. If you are not familiar with arrays, or were working in a language that didn’t support them you might define some variables like this.
What’s the harm you say? Well as you move through the rest of that project you are likely going to find tons of request and requirements that will require a lot of coding with those individual fields. Let’s take the obvious example, calculate total sales for the year.
Not the end of the world, that is until you make one typo, and include JulTotalSales twice, instead of June and July. Remember every bit of code you create is one more opportunity to introduce a bug.
There are lot of other request that would also lead to lots of code and work. How would you display the 12 months of sales as individual rows in a table control? You would have to programatically create each row and reference each individual field. I am to lazy to write that example code! What if some genesis in management decide he wants the system to always show the last twelve rolling months instead? Even ignoring the fact that now your variable names no longer make sense, you are talking about a lot of code (and opportunity for bugs), whenever a new month rolls around and everything needs to slide down one.
So lets look at how arrays would help us with all of the above. First since we know we are dealing with a fixed number of months (unless they Mayan calendar makes a comeback!), so we declare a fixed array with 12 elements.
And that TotalSales calculation?
And what about that table control that I was to lazy to even write the example code? In WX, we can use an array as the source for our table control.
Absolutely no code at all and I am displaying the 12 months of sales in a table control!
Bet you can stump me with that rolling twelve month example do you? Well I admit it is a little tougher. It takes two lines of code.
Line 41 delete the element at subscript position 1, which causes everything else to slide down one. So what was in position 2 is not in position 1, etc. The Line 42 adds a new element to the end, which in this case would be subscription 12, since after deleting an element we only had 11. And as a bonus my variable names still make sense!
So in four lines of code, we accomplished all of the examples requirements. Without arrays it takes 12 lines of code just to define the variables!!!!
Are you starting to see why I am addicted? And are still at the PBR level of arrays!
A couple of quick array tips and shortcuts that WX provides.
When populating arrays you can use a handy short hand. These statements declares an array of 12 strings and then populates it with the names of the months. I use this syntax a lot when defining “fixed” lookups for combo boxes, for things like, Month Name, Salutation, Status Codes, etc.
Looping through an array. In most languages you would write something like this, which also works in WX
However we also have the browsing syntax available, which will come in handy when we start dealing with structured arrays.
Notice that MonthlyTotal is not declared anywhere. It is dynamically declared based on the array definition. In this instance MonthlySales is an array of currency, therefore MonthlyTotal is a currency variable. This will loop through each element of MonthlySales. As I said in this simple example its not much different but when we get into structured arrays, it will really come in handy.
But what if you need the subscript for some reason? Well you could create a local variable and increment it with each pass, but there is another form of the For Each browsing statement that is an even better approach.
Now with each pass, MonthlyTotal has the elements value, and subscript contains the subscript, so the first pass it will be 1, and second will be 2 and so forth. When we discuss Associative arrays you will see just how handy this syntax is!
Our examples so far were with fixed arrays, meaning we declared exactly how many elements would exists. You can also declare arrays as dynamic meaning you don’t know how many elements will exists. The definition is very easy you just leave out how many elements it will hold, like this.
Now you can add an element by using ArrayAdd, as you saw in one of the examples above.
You do have to be careful with this style of array. For instance if I tried to reference subscript  of DynArray right now
I would get a runtime error because there are only 2 elements in the array. Most runtime errors caused by arrays involve trying to reference an element that doesn’t exists! WX gives us a way to handle this by defining a growth value.
This means that if you reference 1 greater than the number of elements it will be created. With this definition and the above code this would not cause a runtime error.
Because there are two elements, and it is set to grow by 1, then it will dynamically create the 3rd element. Note its value will be an empty string.
Would still generate a Runtime error because the growth setting is only 1. So you can only reference one greater than the current number of elements. But we can handle this situation as well. By declaring our array as follows
This means that the number of elements will increase by however many are needed, so now the following statement does not generate a runtime error.
Not there is a caveat to this though. It created all of the elements needed. So lets say we tried to reference element subscript 1,000,000, well we just dynamically created a million variables, which could definitely effect memory and therefore performance. There is another, more sophisticated way to accomplish this using Associative arrays. We will cover those later.
Sorting arrays is another handy feature. Let’s take this definition.
If we issue this command we can rearrange the elements.
And now the elements are arranged in Alphabetic order.
The sort command can be far more complex that that as you will see when we discuss structured arrays.
With v19 you can even randomly sort the array.
And get this
Sadly I hadn’t notice this new function, and have written a far more complicated method of sorting my arrays for TeamAlogy.com. Just another lesson in just how verbose and advanced the WX language is!
You can seek on an array to find out if one of the elements contains the value you are looking for. Take our original Unsorted array. If we do this
We will get 3, telling us that the element at subscript 3 contains an “F”. If we instead search for “Z”
The result is -1 mean none of the elements contain a “Z”. Just like the sort function, the Seek function also can be far more advanced as you will see with structured arrays.
There is a ton of different functions for adding and deleting elements in arrays, and I won’t cover them here: The pcSoft online help on the subject is a good place to start.
There is also very helpful function like ArrayToCSV and ArrayToString, and their opposites CSVToArray and StringToArray, that you should check out in the online help, as we won’t be covering them today, but I have covered them in the past. Also be sure to check out TableToArray as covered in this article
Let’s expand on our sales example one more time, then I promise you I will break out the crack pipe!
Multiple Dimensional Arrays
That genesis in management now whats 3 years of sales tracked and displayed instead of just 1. We won’t go into how we would do that without arrays, lets just say it would involve adding Year1, Year2, Year3 to all of our variable names and a wheelbarrow full of coding! With arrays our task is much easier. We just add a second dimension to our array.
That declares are array as a matrix, with 12 rows, and 3 columns. WX supports up to 10 dimensions for an array, it makes my head hurt to even think about why you would ever need an array with 10 dimensions. Referencing the elements of a multiple dimension array is fairly straight forward, you just have more than one subscript. For instance if you need to see the value of August (Month 8) for Year 2 it would be:
Now I have a confession to make. I rarely use multiple dimension arrays. They have their place, I am sure, but there are limitations to them. They are not as easy to use in a table control for instance. Since you are still declaring a simple array, all of the elements have to be the same type. Generally, I find that at the point I am considering multiple dimensions, what I am tracking is more complicated and structure arrays accomplish the task instead. This isn’t meant to discourage you from using them, just an explanation of why I haven’t covered them any deeper.
Time to break out the good stuff. Associative arrays are basically indexed arrays, instead of using a numerically incrementing subscript, they are stored and indexed by another value. At first this distinction doesn’t sound like much but lets look at a real world example. TeamAlogy allows the configuration of user defined attributes for Survey participants, such as Program Type (After School, Summer Day Camp, Encore Clubs, etc). Then we provide the survey results based on the values of that attribute. So you can see if After School customers are happier than Summer Day Camp customers etc. Since all of this (including the values) is user configurable, the code to sum up the results and split them up needs to be very generic. A simple example of this would be we want to know how many Customers of each Program Type answer where involved in the survey, as we will need this number later to calculate averages.
Line 3 declares are array and at first blush looks fairly complicated versus what we have seen thus far, but when we break it down its really not that bad. We have the new keyword associative which simple says this isn’t your Grandfathers array definition! Next we have 3 options specified and technically these are the default values for the 3 so we could have done without them if we wanted to.
First we don’t want any duplicate keys, so there won’t be two elements with the same key, I can’t honestly think of a reason why I would ever use an associative array with duplicates, as you would lose the whole advantage of being able to directly access the values.
Next we set the default value, by default is is the default value of whatever the definition is, in this case its an int and therefore 0. But say I wanted to avoid divide by zero issues later, I could set the value default value to 1. Then if I reference a key that didn’t exists, it would return 1, instead of 0. So the below statement would return 1 instead of 0. Notice you don’t get an error that the key doesn’t exists, you just get the default value, so if you logic depends on knowing if the key really exists, you might want to make the default value -1 or something similar that you know would never exists other than as a default value.
Next we define the definition of the key. In this case we are using a string as the key, which again is the default. If needed we could use a int instead or any of a number of the simple data types. See the online help for more details.
This method of referencing elements is big difference between a regular array and an associative array. Now you can randomly reference elements instead of in numerically order. Remember our growth example, and the warning I gave you that referencing subscript 1,000,000 would create a million elements? Well if we declare it as an associative array with an int of the key, then we could reference 1,000,000 and it would only add one more elements to the array, instead of a million!
Back to our example, Line 5 and 6, are just executing a query to get the request for a specific survey. The Line 8 loops through the results of that query.
Line 9 is where all the magic happens. Notice we are using the value of Attribute1 as the key. So if on the first pass, Attribute1 equals “After School” then the reference to it will be 0 (our default value) and the +=1 will make it 1, and now the element actually exists in memory.
Say a few more passes take place and we hit another instance of Attribute1 equals “After School”. This time Attribute1Count[“After School”] equals one, so +=1 sets it to 2, and so on. This logic could be duplicated without associative arrays but it would require a couple of different arrays. One to keep track of the subscript of each attribute value, and then another to hold the totals, and each loop would have to do a seek on the first array, in order to know the value of the subscript to set in the second array. As you can image this is quite a bit more code and performance would likely not be as good.
Running the code gives us a result like below.
One of the downsides of associative arrays use to be the fact you could not sort them. But that has been eliminated with v19. There are two different ways we can sort the array. The first is a syntax we already used earlier
This will sort the array based on the value of the elements. However if you run the code and look at the array in the debugger you will see that it looks exactly the same as it did before. What’s the deal did Uncle Pete lie to you? Of course not! The debugger is lying to you and I can prove it.
Let’s add this code to our sample.
This loops through the associate array and builds a string with both the key value and the element value. Remember the browsing syntax we discussed for using the subscript, and I said it would come in handy on associative arrays! It sure did, didn’t it!
If we run this code we will see that indeed our sort has changed the order of the associative array. But for whatever reason the debugger doesn’t show that to us.
We can also sort the array based on the key values.
And indeed we will see that the results are sorted based on the key values instead of the element values.
Note: Again the debugger doesn’t show the results of the sort however.
Prepare to never be able to put the array pipe down again!
Remember way back at the beginning of this article, what did we say an array was?
“An array is variable that has multiple instances”
Well so far we have been dealing with simple data types (int, string, currency, etc.). But at the end of the day, just what are structures and even classes? They are variable types, they are very complex variable types, but they are variable types. That means we can create arrays of structures and classes! The train is getting ready to head to crazy town, so climb aboard!
Let’s start with a quick discussion of just what a structure is. A structure is a complex data type, made up of other data types. So let’s go back to our sales example. At the point we were tracking 3 years of monthly sales, it was starting to get messy. Imagine if that genies in management finally realizes that he should also track expenses, not just sales. Well we could create another array for expense, and then keep them in sync, but that sure sounds like it would be an easy way to introduce a bug and get the arrays out of sync. So we are going to create a structure to group all the data for a month together.
What did we just do? We created a new Variable type. That is a complex variable type made up of several other variables types. But one important distinction to understand is this is a variable type, not a variable its self. Which means we can store data in the structure we first need to create a variable of that type.
Now we have a real variable that we can store data in. But how? You do it everyday in WX, without realizing it. The properties of controls, columns of a table, etc., when you really boil it down they are structures and we use the . syntax to access them.
OK, that’s kind of cool but it other than giving us a logical grouping of variables it hasn’t really done a lot for us … or has it?
Remember OneMonthsSales is a single variable, that means that it can be passed to a procedure or more importantly returned from a procedure. So by creating a structure you can pass a considerable about of data to a procedure without having a bunch of parameters that you have to make sure you get in the correct order, etc. And with intellisense it is very easy to get the parts of the structure right, once you type the . you see a list of all the individual variables that make up the structure.
Another handy thing you can do is clear the entire structure at once.
One last thing I will mention about structures, you can do an assignment by copy. to populate dissimilar structures. For instance what if we had already had a structure with just the sales info in it and we wanted to move it into our structure that includes expenses and profit?
That is going to give us a runtime error. Because they are not the same variable type.
But before you go coding individual assignment statements for all the pieces, take a look at assignment by copy.
What assignment by copy does is matches up the individual variables inside each structure based on their names. So the variables that are the same get populated, the others do not. There is even a way to provide a “translation” so that variables with different names can be matched up, but assignment by copy is a topic for another day, but I definitely encourage you to check it out, because once you start using structures it sure comes in handy.
Finally a Structured Array
So finally, finally we are ready to declare a structured array. You can probably guess the definition code on your own, that is as long as you don’t try to over think it.
Its just an array of a variable type, the same definition we have been doing all along, its just happens to be a complex variable type. This is an important concept to understand. It just just an array (multiple instances of a variable) so if you forget that its a complex variable type all the logic of how and why you work with arrays is still the same, just cranked up several notches.
You might have thought that the definition would have been an array of OneMonthsSales, but OneMonthsSales is a variable, not a variable type.
So lets get some data into our new fancy array.
Before the loop begins I just populate the SalesMonth so I can increment it 1 month for each pass. Then we are populating the array with 36 months of data.
Nothing fancy about any of the assignment statements (150 to 153), except maybe the data incrementing, and the fact that we are using the individual pieces of our structure, but you already know how to do that now right!
And then our ArrayAdd at 154, looks the same as the ArrayAdd we used before doesn’t it, because it is!. It is adding an element to the array. In this case the array is a complex data type, so the value we are adding has to be that same complex data type, but that is it.
And if we look at the debugger we will see that we have our array and each element is made up of the individual variables of the structure.
Remember when we created a multi-dimensional array to hold the multiple years of sales, but it was difficult to use to populate a table control. Not the case with a structured array!
If we look at the content of our array, its just based on our Structured Array.
But because it is a structured array we are able to link each column to its individual variable in the structure.
And just like that we have a very nice table control, no coding involved!
So let’s talk sorting!
First a quick tweak to our loop to populate the array so we can get some random data.
The we can sort by any of the pieces of our structure.
Notice our ArraySort statement is a little different. AsMember say to use the Members (pieces of the structure). What is great about this we can get real fancy. Say we want to see our most profitable months, but if there are two months with the same profit we want to see the month with the highest sales first.
That says sort descending by Total profit, then TotalSales. Note the ; between the two field names, a common mistake I still make is trying to use a , instead!
To be Continued
We still need to cover Seeking on Structured arrays, some additional syntax that is available to use for structured arrays, and then get wild and crazy and talk about associative structured arrays. With a few more real world examples. So we will cover all of that next week, which will leave you primed for all of the foundational pieces that make up the Andy’s File Manger Classes, so he can pick the ball after that and take it home! So be sure to check back next week for the remainder of the structured array coverage.
Be sure to go over to wxLive.us and watch the Uncle Pete’s corner webinar that is the companion to this article.
Uncle Pete’s Corner is webinar series on all things WX, to watch the recorded version of this webinar, many other WX related webinars or to watch future ones live go to wxLive.us