Chapter 11. Arrays

CONTENTS
  •  Array Overview
  •  Array Creation and Manipulation
  •  Loops
  •  Associative Arrays
  •  Summary

Using and understanding arrays is a milestone concept that will catapult you into another level of programming skill. It's almost like the difference between riding a one-speed and a 10-speed bicycle between a piece of toast and a waffle or between playing checkers and chess. I'm just saying arrays are a big deal. Your programming career can be separated as "before arrays" and "after arrays." Unlike the way we've used variables so far (to store one value at a time), arrays enable you to store as many individual values as you want and of any data type. In addition to storing a lot of information in one place (an array variable), you can quickly access any individual item to see or change its value. Furthermore, the Array object has a host of methods that let you perform fancy operations on the contents of an array. For example, you can almost instantaneously sort the contents alphabetically if you want.

Although arrays are not the most advanced feature of ActionScript, they are perhaps the most valuable because they're so easy to use. If storing values in variables is convenient, storing lots of values in one array is invaluable. In addition to the useful operations you can do with arrays, you'll find that the syntax you learn (as with so much other syntax) is easily applicable to other parts of ActionScript.

In this chapter, you will:

  • Learn how arrays work and their benefits.

  • Build arrays using several techniques.

  • Access individual items in arrays (to ascertain or change their values).

  • Explore all the methods for the Array object.

  • Learn how to create and use associative arrays (also called generic objects).

Unlike many topics covered in this book, we'll actually go through practically every detail of using arrays in ActionScript. For example, I've left many topics out of this book because they are either unlikely to ever serve you or they can be picked up easily based on the foundation knowledge you're acquiring. In the case of arrays, it's quite possible you'll use every conceivable aspect. For that reason, this chapter is quite detailed.

Array Overview

Arrays are just another data type. Variables can contain strings, numbers, or other data types, including arrays. Just as you can do "string" types of things to variables containing strings and "number" types of things to variables containing numbers, when your variable's value is an array, there is a unique set of "array" operations that you can execute. Strings and numbers are very familiar. Let's take an overview of the way arrays work.

The idea with arrays is that any one variable can contain multiple values each in a unique address within the variable. Compare a plain variable containing a number or string to a studio apartment or single-person home. Assuming that only one person lives in any given apartment at a time, it's like a variable containing a string. You can replace the value in the variable or replace the person living in the apartment but only one is in the variable or apartment at a time. If a regular variable is like a single apartment, an array is like an apartment building (see Figure 11.1). There might be 50 individual apartments in an entire building. Similarly, an array can have 50 individual locations for data (called indexes). Replacing the value in any one index is like replacing the resident in any one apartment.

Figure 11.1. If a variable is like a house that holds one person, an array is like an apartment building that holds many individuals.

graphics/11fig01.gif

To continue with the apartment building analogy, it's possible to put different types of people in the different apartments. One apartment could even be used to store cleaning supplies. The storage concept is similar with an array. You could store a string in the first index of an array. In the second index, you could store a number. What's really wild is that you can store any kind of data type in the individual indexes of an array including arrays! That is, in the third index, you might have an array of 10 separate numbers. Compare this to one apartment being converted to bunk beds where four separate people could sleep (see Figure 11.2).

Figure 11.2. Each apartment (or index of an array) can contain several people (just as one index of an array can contain another array).

graphics/11fig02.gif

Unlike apartment buildings, you can structure arrays without regard to physical limits. Your master array, for example, could be made up of 10 arrays, each containing 3 items. Each of the 10 arrays could contain students' personal information for example, first name, last name, and age. If you decide to add a forth piece of information for each student, your 10 arrays will simply contain four items each. Consider the data in the table shown in Figure 11.3. All this data can be stored in one array variable. You could store an array of information in that fourth index perhaps all the individual scores that each student has received on tests taken. Even if you don't have the same number of scores for each student, an array is perfectly suitable.

Figure 11.3. All 10 rows of four columns each can be stored in one array variable.

graphics/11fig03.gif

Arrays full of arrays (or "nested arrays") might seem complicated, but they actually help keep things organized in a couple of ways. First of all, you can structure the data however you choose. You could have an array with 10 sub-arrays, each of which contain 4 items (as in the 10 rows of 4 columns in Figure 11.3). Or, if you choose, you could have an array containing 4 items, which each contain 10 items each (as Figure 11.4 illustrates with its 4 rows of 10 columns). In this way, you structure data in a way that makes the most sense based on the nature of the data.

Figure 11.4. You can structure data in any format you want.

graphics/11fig04.gif

Another way that arrays (and nested arrays) keep things organized is by reducing the need for superfluous variables. Consider the relatively simple idea of 10 students each with 4 bits of information. To store all that information without arrays, you'd need 40 separate variables perhaps something like:

firstName_student1="Phillip";  lastName_student1="Kerman";  age_student1=37;  score_student1=100;  firstName_student2="Joe";  lastName_student2="Smith";  age_student2=28;  score_student2=89;  //and so on

Not only do you need to develop a workable naming convention, but you have to keep track of it. That is, you can't try to use the variable student1_firstName because the earlier convention was firstName_student1. With 40 separate variable names, there's a lot to track and many places to have problems. Arrays eliminate such issues entirely.

Array Creation and Manipulation

When you can see the benefits of arrays and you decide how to structure the data, you need to learn how to populate the array with the various values. After an array is created, you'll need to learn how to manipulate its contents. Not only can you search and sort an array, but you also can add more items. This section covers how to create and manipulate arrays.

Creating and Populating Arrays

There are several ways to create a variable that contains an array. You can simply initialize a new variable as an array without contents, and populate the array later. Similarly, you can initialize an array of a specific dimension (that is, with a certain number of indexes available). Finally, by assigning a variable's value to data that's in the form of an array, you can create and populate an array in one move. We'll look at all three ways to create arrays as well as how to add to an array already in existence.

The most basic form to make a variable become an array is:

myArray=new Array();

This assigns the custom variable myArray the value of an empty array. The only thing this accomplishes is to prepare myArray so that you can start populating it. Although it's not the same thing, I think of this method as similar to the statement var myLocalVariable. All that statement does is say, "I'm about to start using a variable called myLocalVariable and it's going to be local." It only makes sense to use the new Array() technique when you know you're going to need an array, but you don't know any of its contents yet. This just gets you set up with an array that you can mess with later.

Another and, in my opinion, counterintuitive way to initialize an array is the form:

myArray=new Array(totalItems);

For example, dozenThings=new Array(12). The parameter passed in parentheses establishes how many items are in this array. The 12 items in dozenThings are null. The reason that I find this technique counterintuitive is that (as you'll see shortly) when you include multiple parameters in the parentheses, it behaves as if you were creating and populating it in one step.

To create a new array and populate it with two or more items, use:

stackedArray=new Array("first item", "second item", "third");

You can include as many initial values in the array as you want, but you must have more than one because a single item would indicate that you want to specify how many items an empty array should have (as above). For example, unluckyNum=new Array(13) could have unexpected results (because it will just create a new array with 13 empty items).

Finally, the most direct (and probably the most intuitive) way to put an array into a variable is in the literal form:

myArray=["item1","item2","item3"];

The brackets say to Flash, "These items are in the form of an array." You could also initialize an empty array as myArray=[]. This literal technique is the way that I usually create arrays (and it's the way I'll do it throughout the rest of this book).

Before we move on to accessing information stored in an array, let's talk about the data types you can put into the various indexes of an array. So far, I've been placing strings and numbers (strings being shown between quotation marks). Naturally, you can use variable names instead, and the current value for those variables will be in their place. For instance, names=[firstname,lastname] places the value for the variables firstname and lastname into the two indexes of the array names. You can also put references to clip instances, as in myClips=[box,circle,_root.otherClip]. If box and circle are clip instance names (in the current timeline) and a clip called otherClip exists in the main timeline, you've just stored references not to the string versions of clip names, but to the clips themselves. When you learn in the next section ("Accessing Array Contents") how to access one index at a time, you'll be able to refer to those clips and perform any operation that you can do when referencing clips directly.

In addition to strings, numbers, and instance references, you can add references to arrays. In this way, you can make an array full of arrays! Consider the following code sequence:

washingtonPolitics=["Governor Hansen", "Sec. White", "Attorney Meier"];  oregonPolitics=["Governor Jones", "Sec. Stevens", "Attorney Philips"];  californiaPolitics=["Governor Black", "Sec. Jackson", "Attorney Smith"];  westCoast=[washingtonPolitics,oregonPolitics,californiaPolitics];

In the last line, the variable westCoast is assigned the value of an array with three items. It just so happens that the data type for each of these three items is array. As you're about to learn, you can quickly refer to the Governor of California by saying you want the first item in the third index of the westCoast array. (In reality, because arrays are zero-based, you start counting with zero, so you'd have to retrieve the zero item in the second index.) With this first example of an array full of arrays, I feel compelled to remind you that because arrays are reference variables (not primitive), they're copied by reference. Therefore, in this example, even after the variable westCoast is assigned, if you ever change any of the three "politics" arrays, you'll also be changing the contents of the respective index in westCoast. (If necessary, you can review the differences between primitive and reference variables in Chapter 4, "Basic Programming in Flash.")

Accessing Array Contents

As you just saw, populating arrays is pretty easy. Accessing the contents of previously created arrays is even easier. To access a particular array's contents, you need to be familiar with how that array is structured. For example, Figure 11.5 shows (on top) how our previous west coast politician example was structured: three arrays, one for each state. Just as easily, we could have had three arrays, but one for each job title instead. It doesn't really matter how we structure it, but when accessing individual items, it's important to first know the structure.

Figure 11.5 You can structure the same data however you want. The example on top is no better or worse than the one on the bottom.
[["Governor Hansen", "Secretary White", "Attorney Meier"],  ["Governor Jones", "Secretary Stevens", "Attorney Philips"],  ["Governor Black", "Secretary Jackson", "Attorney Smith"]]  [["Governor Hansen", "Governor Jones", "Governor Black"],  ["Secretary White", "Secretary Stevens", "Secretary Jackson"],  ["Attorney Meier", "Attorney Philips", "Attorney Smith"]]

Now that you are familiar with the array's structure, you'll find accessing items in the array (to see their values or change them) is very easy. We'll look at how to access individual indexes directly, and then look at how to use loops to step through all the indexes of an array.

Direct Access

Array items are accessed using what's called bracket access. The following expression will return the item in the first (the zero) index:

myArray[0];

Therefore, if myArray is first assigned the values using myArray=["apples","oranges","bananas"], the expression myArray[0] returns "apples". Simple, isn't it? The only thing to mess you up is the fact that you must start counting with zero. I probably don't need to remind you (but I will) that this bracket access simply produces an expression that returns the value in that index but there's no assignment or change to the array. You could copy the second thing in myArray into another variable, as follows:

secondFruit=myArray[1];

secondFruit will be assigned the value "oranges", but nothing changes in myArray.

To access the contents of an index in an array that's inside another array, the form is extended to:

mainArray[indexOfSubArray][indexInSubArray];

Consider the following example:

bookDataOne=["Teach Yourself", 587, 24.99];//title, page count, price  bookDataTwo=["ActionScripting", 618, 39.99];  allBooks=[bookDataOne,bookDataTwo];

After you understand the structure used (title, page count, price) to access the price for the first book, you can use allBooks[0][2], or for the price of the second book, use allBooks[1][2]. To access the title of the first book, use allBooks[0][0]. If this way of referencing items in arrays inside arrays is confusing, consider looking at it in pieces. For example, to access the entire array for book 2, you'd simply use allBooks[1]. To access just the price from the simple array bookDataTwo,you'd use bookDataTwo[2]. But what is allBooks[1], anyway? It's the same thing as bookDataTwo. Because allBooks[1] and bookDataTwo are interchangeable, you can say allBooks[1][2], and it's the same as saying bookDataTwo[2]. Because such nested bracket references use left-to-right associativity, allBooks[1][2] first performs the first part (allBooks[1]) and that turns into the array that then has its second index referenced.

What's cool about bracket reference is that you can use it for more than just accessing the contents of arrays. You can also refer to items and change them through assignments. For example, allBooks[1][1]=525 assigns the value of 525 to the second item in the second array, so that 500 changes to 525.

There's not much more to say about accessing the contents of arrays. Perhaps a few examples will solidify these points. Using the original data in allBooks, consider the following maneuvers.

The following expression returns a number that is 10 percent less than the price of the first book:

allBooks[0][2]*.9;

If you want an expression that rounds off to the nearest cent (that is, the one-hundredth decimal), use:

Math.round(100* allBooks[0][2]*.9)/100;

Notice that I multiply by 100, round off, and then divide by 100. The only flaw in this solution is that there are no trailing zeros; for 24.50, you'll get 24.5. To pad with trailing zeros, you'll have to convert to a string (and possibly use the String object's lastIndexOf() method and length property). Workshop Chapter 8, "Creating a Currency Exchange Calculator," covers how to perform these tricks.

The following statement takes 10 percent off the price of the first book and changes it in the array:

allBooks[0][2]= Math.round(100* allBooks[0][2]*.9)/100;

Loops

Even though accessing individual items in arrays is quite common, you'll also have the need to access each item in sequence. You already saw how to create an empty array or populate one. Later in this chapter, you'll learn all the methods available to add to, or otherwise change, arrays. If you populate your array by hand, going through each item is not much of a challenge because you'll know how many items are present. However, you might often add to arrays and never know precisely which items are present. In such cases, it's easiest to use a loop to go through each item. Even when you know how many items are in your array, a loop is often more efficient than writing a separate line of code to access each item individually.

Suppose, for example, that you have an array called vowels that is initialized as follows:

vowels=["A", "E", "I", "O", "U"];

A hard-wired loop (not ideal) could look like this:

for(i=0;i<5;i++){   trace ("One vowel is " + vowels[i]);  }

Notice that using vowels[i] will extract items 0 through 4 from the vowels array as i varies from 0 through 4. This example is hard-wired because the 5 means that the loop will always repeat five times (even if you add more items to the array later). You can improve upon this loop by using the for in loop instead. A for in loop will automatically step through all the items in an array.

for (i in vowels){   trace ("One vowel is " + vowels[i]);  }

This alternative is easier because you don't have to include the three elements in a regular for loop namely, "init" (or i=0), "condition" (i<5), and "next" (i++). (We covered these when we first looked at the for loop in Chapter 5, "Programming Structures.") The for in loop has a couple of funky attributes. First, nowhere do you specify that the iterant (i in these cases) will increment. In fact, the iterant actually decrements. It automatically begins at the highest index value (4, in the case of the vowels array) and then decrements through all the items in the array to zero. Often the fact that a for in loop goes through the array in reverse order is not an issue. Just realize it works this way. Finally, to be fair, the initial for loop I showed could be less hard-wired if, in place of 5, you used an expression that resulted in the number of items in the array. You'll see later in this chapter that you can use the length property (which you learned with the String object) in exactly this way; vowels.length results in 5.

Here's a quick example of a function that goes through the string passed as a parameter and replaces all vowels with an uppercase version of that character. So, changeVowels("Phillip Kerman") turns into "PhIllIP KErmAn" (notice that "P" and "K" were already uppercase).

 1 function changeVowels(aString){  2   var vowels=["A", "E", "I", "O", "U"];   3   var total= aString.length;   4   for (var spot=0; spot<total; spot++){  5     var aLetter=aString.charAt(spot).toUpperCase();   6     var vowelFound=0;   7   8     for (var i in vowels){  9       if (aLetter==vowels[i]){ 10         vowelFound=1;  11         break;  12       }  13     }  14     if (vowelFound){ 15       aString=aString.substr(0,spot) + aLetter + aString.substring(spot+1);  16     }  17   }  18   return aString;  19 }

This example is worth walking through despite having arguably no practical use. (I mean, how many times will you need to capitalize all the vowels in a string?) Anyway, after the array of vowels is created in line 2, I store the string's length in a variable total. Then, a for loop in line 4 goes through all the characters in the provided string (aString), one spot at a time. The only reason I used the variable total instead of an expression (aString.length) in the "condition" of the loop is that Flash will actually calculate that value in every iteration. It's faster this way. For every iteration of the main for loop, the charAt the current spot is converted to uppercase and placed in the variable aLetter. Then the variable vowelFound is set to 0 (assuming at the start that the aLetter is not a vowel but, of course, if it is, we'll find out shortly). While this one letter (aLetter) is being analyzed, we start another loop. This time, we use a for in loop to go through all the items in the array of vowels (vowels). For every item in vowels, we check whether aLetter==vowels[i] (where i iterates from 4 down to 0). Who cares that it's going backwards because as soon as we find a match, we set vowelFound to 1 and break out of the current loop (the one looping through all the items in the vowels array). So every time, the loop either finishes naturally and leaves vowelFound set to 0, or finishes early and vowelFound is set to 1. In any case, the next if statement performs a string replacement (of the letter in the current spot) if vowelFound is 1. Notice that I don't bother extracting the letter in the current spot and changing it to uppercase, because I already did that when I first assigned aLetter's value. Also notice that even letters that turned out not to be vowels were converted to uppercase because when comparing them to each item in vowels, they needed to be uppercase to match (as the vowels array contained all uppercase).

As an overview, this function steps through each character in the given string, compares this character to each item in an array of vowels, and when a match is found, makes a replacement to the given string. You'll have many more opportunities to write loops that work on strings (including the one that turns a number into a dollar value in Workshop Chapter 8) and loops that work on arrays (when you need to loop through every clip on the Stage to see which one the mouse is covering in Workshop Chapter 6, "Working with Odd-Shaped Clickable Areas").

Array Object Methods

Populating arrays and accessing their contents is quite convenient because you can store many different items in one variable. In addition, arrays are convenient because after they're created, ActionScript offers a host of methods that can perform very interesting modifications on that array (see Figure 11.6). Besides the one property (length), almost all the Array object methods will actually modify the array itself. That is, myArray.someMethod() won't return a value; rather, it will change the contents of the array. Of course, "someMethod()" doesn't exist, but the types of methods available can be broken down into three general types: string-related methods, which convert arrays into strings or vice versa; populating methods, which add to or subtract from arrays; and sorting methods, which reorder the contents of your array. We'll look at the methods and related topics in that order.

Figure 11.6. All the methods and properties for the Array object are listed in the Actions panel.

graphics/11fig06.gif

String-Related Methods (length, toString(), split(), concat(), join())

You won't find a listing of string-related array methods anywhere in the Flash help files. The topics you're about to learn are grouped together because I think they are either similar to String object methods or they involve converting strings to or extracting strings from arrays.

The only property available for arrays happens to be the same as the only property available for strings: length. Although this might appear confusing, it's actually quite useful. If the object for which you're trying to get the length happens to be a string, you'll get a count of the number of characters. When the object is an array, you'll get the count of items in the array. Consider assigning a string to a variable as myString="hot" and assigning an array to a variable myArray=["waffles", "pancakes", "cereal"]. The following two expressions return 3:

myString.length;  myArray.length;

There's not much more to it, except to remember two points for both arrays and strings. First, length is a property (not a method) and, as such, doesn't include the parentheses that always follow methods. Second, because both arrays and strings are zero-based, the last character in a string with a "length" of 3 (or the last item in an array with a "length" of 3) is character (or item) 2. Or, for creating dynamic expressions, you can think of the last item in an array as being at the index "length minus 1."

The method called toString() when attached to an array will return a string version of the array. For instance, if a variable contains an array, such as myArray=[1,2,3,4], the expression myArray.toString() will return the string "1,2,3,4". One thing you can do with this method is to quickly take an entire array and get a string that can be placed into a Dynamic Text field. Naturally, you could (with little more work) loop through the entire array and convert each item to a string. Perhaps you want to separate each item (being extracted from the array) with returns so that it appears in a Dynamic Text field like a column of data. As always, you can solve one task in several different ways. You could either use a loop and concatenate each item with "\r" (for return) or write a function that replaces each comma with "\r". The toString() method just gives you a quick and easy way to make a string. (By the way, objects introduced in Chapter 12, "Objects," can also use the toString() method.)

The split() method is actually a method of the String object. The reason I waited until now to introduce it is because this method takes a string (as its object) and returns an array. That is, if you take a string such as myString="Phillip,David,Kerman" and use this method (as in newArray=myString.split()), the result is that newArray is assigned the array value ["Phillip","David","Kerman"]. The split() method uses commas (as a default) to separate the items in the string that is being operated on. If you want the split() method to use a different character as a delimiter instead of the comma, just supply the character as the parameter when using split(delimiter). That is, if your string uses "#" for a delimiter (as in myString="Phillip#David#Kerman"), you can create the same array shown earlier by using newArray=myString.split("#"). This comes in handy when you have an external source of data that you want to use in an array.

graphics/icon02.gif

In Chapter 16, "Interfacing with External Data," you'll learn how to load variables from server scripts, XML data sources, and text files. With the exception of XML data, external sources provide data in the form of strings (not arrays). (Even if you want to load in the numerical value 2, you'll actually get "2" which needs to be converted to a number.) If you simply separate items with commas (or another delimiter), you can use split() to quickly and easily convert this loaded data into an array. In the previous edition of this book, I had to explain that although split() is powerful, it was so slow that sometimes it was problematic to use. Not in Flash MX! All the string, array, and XML parsing routines have been optimized by being written in low-level code. Before, split used actual ActionScript that couldn't be optimized very much. Now, all these methods perform lightning fast.

The last two methods we'll explore in this section might seem identical, but they're actually quite different: join() and concat(). The join() method performs the opposite operation that split() does. That is, join() takes an array, converts each item to a string, separates each item with an optional separator (or comma), and finally returns the string. Actually, join() is almost the same as the toString() method except that with join(), you can optionally specify a different separator besides a comma. So, if a variable is assigned an array value, as in myArray=[1,2,3,4], you perform the assignment stringVersion=myArray.join("_"). The result is that stringVersion will equal "1_2_3_4". If you used stringVersion=myArray.join("\r"), each item would appear on a new line (that is, separated by returns). (When you use join() without a parameter, a comma will be used as the separator.)

The concat() method will either concatenate additional items onto the end of an array or will concatenate two or more arrays (merging them into one). As a method, concat() is always attached to one array and takes (as parameters) the items you want to add to the array. If the parameters are individual values, they are added to the end of the array being operated on. For example:

odds=[1,3,5,7,9];  oddEven=odds.concat(2,4,6,8);

This creates an oddEven array with the value [1,3,5,7,9,2,4,6,8]. I suppose simply adding to the end of an array using concat() is convenient because you can use one line of code to add as many items listed as parameters.

The other way to use concat() is to concatenate one or more entire arrays to the end of another array. Consider this code:

vowels=["a","e","i","o","u"];  other=["y"];  odd=["q","z"]  all=vowels.concat(other,odd);  notOdd=vowels.concat(other);

First, notice that the first three variables contain arrays (even though other is an array with only one item). In the end, the variable all will equal ["a","e","i","o","u","y","q","z"], and notOdd will equal ["a","e","i","o","u","y"]. Notice also that when the parameters are arrays, the items in that array are added to the end of the array being operated on. Finally, you should notice too that the concat() method doesn't actually change the array, but rather returns a new array. I always find this method strange in that regardless of whether I want to add one item, several items, an array, or multiple arrays, I always must concatenate onto a base array. That is, you can't treat concat() like a function and say concat(oneArray,otherArray). Because it's a method, you must say something more like oneArray.concat(otherArray). (By the way, there's an identical concat() method that's used with strings.)

Populating in Order (pop(), push(), shift(), unshift())

The rest of the methods we'll look at are different than all the others discussed so far this chapter. Both the populating methods (this section) and the sorting methods (next section) will actually change the array that they operate on. The methods described earlier in this chapter will return values (either string or array data), but they don't actually change the array at hand.

The best way to understand these methods (pop, push, shift, and unshift) is to think of a spring-loaded tray dispenser that you might find in a cafeteria. You can take a tray from the top of the stack and the next tray below rises to the surface for someone else to take. To replenish the dispenser, you can place one or more trays on top of the stack. Every time a tray is placed back on the stack, all the trays below are pushed down. Think of a starting array like a stack of trays (each item is a separate tray). Then the methods perform the following operations. Taking a tray from the top of the stack is equivalent to the pop() method because it will remove the last item in your array and return it. (That item is returned and the array has that item removed.) Placing trays on the top of the stack is the same as the push() method because it accepts one or more parameters that are placed at the end of the array. The shift() method is the same as pop() except that it removes the first item and returns it (from the bottom of the stack). Finally, unshift() is like push() except that it will add an item (or items) to the beginning of an array and push everything else to later indexes in the array. Check out Figure 11.7.

Figure 11.7. To understand pop, push, shift, and unshift, you should think about a stack of trays.

graphics/11fig07.gif

Although these four methods might seem useful only for a cafeteria simulation, they're actually quite helpful. A great application to consider and one that will help you understand how to use these methods is a universal "Back" button (such as on a browser). Imagine that you give your users the ability to navigate to any section in your movie. For this example, let's say that each section is on a different frame number. In theory, this would be a simple case of pushing the frame number into an array any time a destination was navigated to, and popping the last value off the end of the array any time we wanted to go "back." Of course, it is slightly more involved, but here's what I came up with:

There is a button for each section to which the user can navigate as well as a "Go Back" button. Each navigation button invokes the custom function go() by providing the frame number to navigate to, as follows:

on (release) {     go(5);  }

The "Go Back" button invokes another function, as follows:

on (release){   goBack();  }

Then, I have the following code in the first frame of the movie:

history=new Array();  function go(where){   history.push(where);    gotoAndStop(where);    onScreen_txt.text=history.join("\r");  }  function goBack(){   if (history.length<2){     return;    }    var discardCurrent=history.pop();    go(history.pop());  }

Let me explain. First, the variable history was initialized as an empty array when this frame was reached. (Because the first line is not contained within a function, it executes when the frame is reached.) We will be populating the history array via the go and goBack functions. Then any time the go function is called, the destination frame (given the parameter name where) is pushed onto the end of the history array. The same parameter's value is used to navigate (using gotoAndStop(where)) to the desired section of the movie. Finally, the history array is converted to string (separated by returns) and placed into a Text Field instance named onScreen_txt for testing.

The goback function (with no parameters) starts off with a couple of error checks. First, if the length of the history array is less than 2 (that is, it only has one item), the function is exited using return. If there's only one item in the array, the user is currently in that section and there's nowhere to goBack to.

Next, we strip off the last item in the array (which is the frame that the user is currently viewing) and place it in a local variable, discardCurrent. Actually, I could have just used history.pop() with no assignment, but I want you to remember that the pop() method returns a value regardless of whether you plan to use that value. Lastly, I call the regular go function and pass (as a parameter) the value of the last item in the array. Notice that by using pop() to grab the last item in history,I'm also removing it. If you don't remove the last item, it will be put in there twice because part of the go function's script involves pushing back the item onto the end of the history variable.

Sorting

You just saw how pop(), push(), shift(), and unshift() can add or remove items from an array. Even though the sorting methods that follow don't change how many items are in an array, they most certainly change the array because they reorder it. There are three sorting methods: sort(), sortOn(), and reverse(). (We'll look at sortOn() in the following section, "Associative Arrays.") First, the easy one: reverse(), which simply reverses the order of the items in the array.

myArray=[1,2,3,4];  myArray.reverse();

After these two lines execute, the value of myArray is [4,3,2,1]. Generally, I can't think of many uses for reverse(). First of all, you're the one who designs the structure of the array. You can always deal with an array no matter whether it's forward or reverse. For example, you can choose between pop() and shift() to delete items or between for loops and for in loops. If an array needs to be reversed, I just wonder who put it in the wrong order, but now you know how to reverse the order in one swift move.

The sort() method is actually very powerful. Without any parameters provided, the sort() method will reorder your array in alphabetical order. It's pretty cool.

myFriends=["Dave","Graham","Chandler","Randy","Brad","Darrel"];  myFriends.sort();

After these two lines, myFriend's value becomes ["Brad","Chandler", "Darrel","Dave","Graham","Randy"]. If sort() worked so easy in practice, this would be the end of the section. However, it's super funky in the way it sorts arrays. Specifically, it always sorts alphabetically even if your array contains numbers. For example:

someNums=[4,2,1,444,2222,11111];  someNums.sort();

This turns someNums into [1,11111,2222,2,444,4], which is not exactly numerical order.

Another counterintuitive fact about the sort() method is that it treats uppercase letters with greater importance than lowercase. Consider the following example:

someWords=["Zimbabwe", "zebra", "cat", "apple", "Arizona"];  someWords.sort();

This turns someWords into ["Arizona","Zimbabwe","apple","cat","zebra"].

I'm sure that I don't need to provide more examples to make you want to learn how to make the sort() method perform the way you want. I thought it was confusing at first to really understand how to control the sort() method. The good news is that you can make the sort() method reorder your array by following elaborate rules that you provide. For example, in addition to sorting numerically, or without regard to uppercase and lowercase, you'll see how you can make up your own sorting rules such as sorting by length, or putting all the even numbers first and then the odd numbers. You can make sort() follow any rule you can think of.

The way to write a customized sort is to provide a homemade function as a parameter. One way is to call another function from inside the parameter, as follows:

myArray.sort(customFunction);

Notice that (normally) you always call functions by including opening and closing parentheses after the function name, but not when providing a function as a parameter of the sort() method. The job of customFunction is to set up the rule for comparison (when sorting), and you don't really "call" it the way you normally call a function. In Chapter 13, "Homemade Objects," you'll learn more about supplying a function definition (called a constructor) as a value. This comparison function is detailed in a moment.

Another way is to provide a literal function as a parameter. The way this works is that you write your entire customFunction right inside the parentheses. You can even put it all on one line if you remember to use semicolons to separate "lines" of code. Ignore the code in the function and just check out the form that this literal technique follows:

myArray.sort(function(a,b){return a-b;});

Notice that the portion in parentheses (function(a,b){return a-b;}) is almost identical to any other function you can write. Besides being all crammed onto one line, there's no function name. Because this function is used in only one place and not called from other parts of the movie, you don't need to bother naming the function. Regardless of whether you provide a function name (like the first example) or you write the function literally right inside the parentheses, it works the same. The reason to consider the literal technique is that the comparison function is useful only for establishing the "sorting rule," and will likely never have any other use to you. Writing a function separately (like the first way) is not as efficient because Flash makes that function available for the entire movie. In the remainder of this section, you'll see examples of referring to function names only because I think it's easier to read than having the function squeezed into the parentheses.

This function that I'm calling a "comparison function" serves to establish the rules that the sort method uses to decide how to reorder items in the array. The function always accepts two parameters (by convention, called a and b). You can pretend that these two parameters represent two items in the array that are being compared. Two items because when you sort, you look at two items at a time to decide which one goes first. Say that you want to sort numerically. What if a is 10 and b is 12? Naturally, you want 12 to come after 10 (that is, b after a). What if a is 2 and b is 10? In that case, you'll want a to come first. The comparison function provided to the sort() method has one job: to return a positive number, a zero, or a negative number. The idea is that based on how you want the sort to order things, your comparison function returns a positive number when a should come after b, a negative number when a should go before b, or a zero when you don't care (and Flash will just leave the two items in their original order).

Let's look at the finished version of a comparison function that correctly sorts an array numerically (and then we can try some "what-if" numbers):

myArray.sort(numerical);  function numerical(a,b){   if(a>b){     return 1; //a should come after b    }    if (a<b){     return -1;//a should come before b    }    return 0; //if they get this far, just return 0  }

So, just looking at the comparison function called numerical, imagine that a is 10 and b is 5. The function returns 1, which means a should come after b. So far, so good. If a is 12 and b is 14, -1 is returned, meaning that a should come first. It works!

In fact, although my example function is easy to read, it's unnecessarily wordy. Here's a more concise version that (when you try any "what-if" numbers) works the same:

function numerical(a,b){   return a-b;  }

If a is 10 and b is 5, this function returns -5 (which, by being negative, means that a should go second). As you can see, when you get fancy like this, writing this whole comparison function right inside the sort() method's parentheses is more manageable. myArray.sort(numerical) isn't quite as streamlined as myArray.sort(function(a,b){return a-b;}), especially when you consider the first way requires you have that numerical function as well.

I hope this exhaustive explanation is clear. There are only so many common sorting requirements (alphabetical, numerical, alphabetical without regard to case, and so on) and I've provided those later in this chapter. After you have these, you'll probably never need to really understand how sort() works. But when you want to do some unique sorting, you'll need to write your own comparison function. For example, you might have a requirement that items in an array are sorted by value but they could be saved in U.S. dollars, Canadian dollars, euros, and yen. It's totally possible albeit complicated to write a comparison function that translates all the values into one currency before making the comparison. Check out the example below that moves odd numbers to the beginning of an array for a taste of how you can customize the comparison function for any purpose. The point is that you can do it when you know how sort() works.

Here are a few examples of common sorting needs. You can use any of these in the form myArray.sort(functionName), where functionName is the appropriate comparison function. To really grasp the following examples, try some "what-if" values for a and b and see whether you can determine whether the function returns a positive number, negative number, or zero.

graphics/icon01.gif

Alphabetical without regard to case:

function caseInsensitive(a,b){   return a.toUpperCase() > b.toUpperCase();  }

Reverse numerical:

function reverseNumerical(a,b){   return b-a;  }

By length (shortest strings first):

function byLength(a,b){   return a.length-b.length;  }

Finally, for a slightly odd one this function (when used with the sort() method) puts the array in numerical order but with all the odd numbers first:

function oddFirst(a,b){   aOdd=a%2; //set aOdd to 1 if a is odd    bOdd=b%2; //set bOdd to 1 of b is odd    if (aOdd==0 && bOdd==0){     return a-b;    }    if (aOdd!=0 && bOdd<>0){     return a-b;    }    if (aOdd!=0 && bOdd==0){     return -1;    }    if (aOdd==0 && bOdd<>0){     return 1;    }    return 0;//just in case  }

This final example shows that you really can write a comparison function for unique sorting needs.

Associative Arrays

To appreciate associative arrays, you must fully understand the value of arrays generally. You've learned in this chapter that arrays have two general benefits: You can store multiple pieces of data in a single structured variable, and you can perform interesting operations on the contents of arrays. Unlike arrays, which contain multiple single-item values in their indexes, associative arrays have pairs of values. That is, a regular array could contain three items (for example, "Phillip,""Kerman," and 37), but an associative array could hold three pairs (say, firstname: "Phillip," lastname: "Kerman," and age: 37). The value for the item in the zero index is "Phillip" in either case, but items in any index of an associative array are referred to by their name (also called property) in this case firstname. It doesn't matter in which slot the pair firstname:"Phillip" resides, you can always find it by referring to firstname.

Although associative arrays make your structured data vastly easier to manage (because you'll never need to remember in which index a particular item of data resides), they have one drawback: Associative arrays cannot use the Array object's methods or properties. That includes the sort() method (which really isn't interesting since position is irrelevant) and the length property (which would be nice). You can, however, loop through an associative array by using the for in loop.

The bottom line is that associative arrays are not really "arrays" because the Array object's methods don't work. I prefer to call them "generic objects" but because this is the "Arrays" chapter, you'll hear me interchange the two terms. Let's first look at how to populate an associative array (oops, I mean, generic object), and then we can look at how to access values within the object.

To create a new object and populate it at the same time, use the following form:

myObject={name:"value",nameTwo:"ValueTwo",thingThree:13};

Notice that each item in the object is a pair of name and value. The names are always written literally (with no quotation marks). It's weird because you might expect that such names would exist previously as variables. But in this case, name, nameTwo, and thingThree are all created on-the-fly and sit there with no quotation marks. Also, notice that the entire object is surrounded by curly braces (not the square brackets that you saw with regular arrays), and that each item is separated by commas.

You can access a value from within the object in one of two ways. In either case, you need to use the name associated with that value, not the index within the object. For example, myObject["nameTwo"] will return "ValueTwo". The thing that freaks me out is that even though nameTwo was used verbatim when creating the object, you need to put it within quotation marks when trying to find its value using this (bracket reference) technique. When creating a generic object (associative array), you provide arbitrary names for each item and Flash accepts them. You're really coming up with unique custom property names. When comparing generic objects to arrays, just think of these as names for the indexes. However, when you think of generic objects as objects, think of these names as properties. This should become strikingly obvious when you see the other way to access values of the properties in the object.

I just showed you the bracket reference technique (ObjectName["stringName"]) and said there's another way to access values in the generic object. You can use the familiar object.property technique. For example, myObject.thingThree returns 13 (the value in the index named thingThree in the example earlier). Now it makes sense that names in associative arrays (that is, properties in generic objects same thing) are not strings.

So, there's no difference between placing a string version of the named item between brackets to access the value of a particular item and using the familiar object.property technique. This happens to be identical to the way that you referred to instance names within any timeline. When you knew the instance name, you just used it without quotation marks (such as, _root.someClip). When you wanted to refer to a clip dynamically and you could only do it by creating a string, however, you placed that string within brackets (as in _root["circle_"+curCircle]). Notice that the object.property technique works when you know the clip name; otherwise, you need to use bracket reference. If it doesn't make your brain start melting, realize that every timeline contains an associative array of the clips present in that timeline. Talk about coming around full circle!

We can save some of this discussion of objects for later chapters. Let's step back and look at a few practical examples of building associative arrays (and accessing their contents).

Let's consider another way to store the data from an earlier example:

bookOne={title:"Teach Yourself", pageCount:587, price:24.99};  bookTwo={title:"ActionScripting", pageCount:618, price:39.99};  allBooks=[bookOne,bookTwo];

Without even considering whether this is the ideal structure, look at how amazingly easy it is to remember which item is which. Earlier, if we wanted to find the price, we had to remember "index 2." The page count was "index 1." (Not exactly intuitive, even if you disregard the zero-based counting.) In this case, when looking at either associative array (bookOne or bookTwo), we can access the price in the form:

bookOne["price"];  //or, bookOne.price

Is that easy or what? Because allBooks is still a regular array (one that contains two associative arrays), you can access the nested elements in the ways the following examples show.

To access the first book's page count, use:

allBooks[0]["price"]; //or, allBooks[0].price

To access the second book's title, use:

allBooks[1]["title"]; //or allBooks[1].price

By the way, in addition to accessing values of any item in an associative array, you can change the values. For example, the following changes the price of the first book to 29.99:

allBooks[0]["price"]=29.99; //or, allBooks[0].price=29.99

Finally, you can add new items to an associative array as arbitrarily as you created them in the first place. Consider this example:

myData={first:"Phillip",last:"Kerman",married:true}

Later, I want to add an item:

myData["childCount"]=1;  //or, myData.childCount=1

At this point, myData equals {first:"Phillip",last:"Kerman",married:true,childCount:1}. It doesn't even matter whether the additional index was inserted at the end, because you can only refer to it by name (as in myData["childCount"]).

If you're like me, upon first grasping associative arrays, you're probably thinking that regular arrays are practically worthless. Remember, however, that associative arrays don't give you any access to all the fancy methods and the length properties of regular arrays.

graphics/icon02.gif

There is one new method for regular arrays that combines associative and regular arrays: sortOn(). Generally, it enables you to sort a regular array; however, if that array contains identically structured generic objects, you can specify those objects to be sorted based on the value for any one property. It's easier than it sounds. In a way, the sortOn() method is limited because you must have an array full of generic objects and all those objects have to have the same property (on which you're basing the re-ordering). Take, for example, the now-familiar array of data about my books:

bookOne={title:"Teach Yourself", pageCount:587, price:24.99};  bookTwo={title:"ActionScripting", pageCount:618, price:39.99};  allBooks=[bookOne,bookTwo];

This example follows the rules for sortOn namely, the allBooks array contains several generic objects and they each have the same properties on which we want to sort. Now, to sort the allBooks array based on the value of the title property of each contained object, use:

allBooks.sortOn("title");

Notice that "title" is in quotes. Translated, this says to re-order the contents of allBooks based on the value of each item's "title" property. To reorder the main allBooks array based on page count, use:

allBooks.sortOn("pageCount");

I suppose it would have been logical to discuss sortOn() when we looked at sort(); they both are ways to change the order of a regular array. The trick with sortOn() is that each item in the array has to be a generic object with the same properties (at least they need to contain the property on which you want to sort). Again, it's slightly limited because you're restricted on the structure (for example, you can't have nested arrays), but the performance and convenience of this new method is often worth the effort when appropriate.

Summary

My head hurts! Not from too much technical information, but from being so excited. Arrays are such an eloquent way to store and manipulate data, it's a shame that some people go through life without ever once experiencing their benefits. Seriously, arrays are awesome.

You saw how arrays could be structured to hold lots of discrete data in whatever way you want. Actually, the stage at which you design how to structure your arrays is the most challenging. As far as what you populate your array with, you learned it could be of any data type, including arrays. In this way, you could have an array full of nested arrays. After you populate an array with data, it is a snap to access and see or change any particular item. Looping through an array is a breeze, too.

This chapter detailed how to use every method made for arrays. Without repeating them all now, just remember that you can manipulate your arrays of data in any manner you want.

Finally, just to make things out of this world, we looked at the topic of associative arrays (also known as generic objects). Although these arrays lack the methods that regular arrays offer, they give you a way to store name/value pairs within each index of an array. This knowledge also will apply to topics covered in the next two chapters.

Just think, an entire chapter without any puns such as, "Arrays offer an array of great features."

CONTENTS


ActionScripting in MacromediaR FlashT MX
ActionScripting in MacromediaR FlashT MX
ISBN: N/A
EAN: N/A
Year: 2001
Pages: 41

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net