We've now seen how array methods can be used to remove elements from and add elements to arrays. ActionScript also offers built-in methods for reordering and sorting elements, converting array elements to strings, and extracting arrays from other arrays.
As its name suggests, the reverse( ) method reverses the order of the elements of an array. This method is simple but impressive. Here's the syntax:
arrayName.reverse()
And here are the results:
var x = [1, 2, 3, 4]; x.reverse(); trace(x); // Displays: "4,3,2,1"
We typically use reverse( ) to reorder a sorted list. For example, if we have a list of products sorted by ascending price, we can display them from least to most expensive, or we can reverse the list to display them from most to least expensive.
Reader Exercise: Try to write your own custom function to reverse the elements in an array. Not only is it harder than it looks, you'll most likely find that the built-in reverse( ) method is substantially faster.
The sort( ) method rearranges elements in an array according to an arbitrary rule that we provide. If we provide no rule, sort( ) places the elements in (roughly) alphabetical order by default. Sorting an array alphabetically is really easy, so let's first see how that works:
arrayName.sort()
When we invoke an array's sort( ) method with no arguments, its elements are temporarily converted to strings and sorted according to their Unicode code points (equivalent to ASCII values for code points below 128). For the code points of most western European languages, see Appendix B. See also Section 4.6.2.2 in Chapter 4 for important details.
// This works as expected... var animals = ["zebra", "ape"]; animals.sort(); trace(animals); // Displays: "ape,zebra" // Cool! What a handy little method. // Watch out, the sort order is not strictly alphabetical... // The capital "Z" in zebra comes before the lowercase "a" in "ape" var animals = ["Zebra", "ape"]; animals.sort(); trace(animals); // Displays: "Zebra,ape". Oops. See Appendix B.
We can also use sort( ) to organize array elements according to a rule of our own choosing. This technique is a little trickier to work with, but it's quite powerful. We start by creating a compare function that dictates how the interpreter should sort any two elements of an array. We then pass that function to the sort( ) method when we call it, like this:
arrayName.sort(compareFunction)
where compareFunction is the name of the function that tells the interpreter how to make its sorting decisions.
To build a compare function, we start with a new function that accepts two arguments (these represent any two elements in the array). In the function's body, we determine, however we see fit, which of the elements we'd like to appear earlier in the element list after a sort operation. If we want the first element to appear before the second element, we return a negative number from our function. If we want the first element to appear after the second element, we return a positive number from our function. If we want the elements to be left in the same positions, we return 0 from our function. In pseudocode, the approach generically looks like this:
function compareElements (element1, element2) { if (element1 should appear before element2) { return -1; } else if (element1 should appear after element2) { return 1; } else { return 0; // The elements should be left alone } }
For example, to put elements in ascending numeric order, we can use a function like this:
function sortAscendingNumbers (element1, element2) { if (element1 < element2) { return -1; } else if (element1 > element2) { return 1; } else { return 0; // The elements are equal } } // Now that our compare function is ready, let's try it out var x = [34, 55, 33, 1, 100]; x.sort(sortAscendingNumbers); trace(x); // Displays: "1,33,34,55,100"
Numeric sorting functions can actually be phrased much more succinctly. The preceding sortAscendingNumbers( ) function can also be written as:
function sortAscendingNumbers (element1, element2) { return element1 - element2; }
In our optimized version, a negative number is returned if element1 is less than element2, a positive number is returned if element1 is greater than element2, and a 0 is returned if the two are equal. Now that's elegant! Here is a version to perform a descending sort:
function sortDescendingNumbers (element1, element2) { return element2 - element1; }
Example 11-6 shows a compare function that adjusts the default alphabetic comparison behavior of sort( ) so that upper- and lowercase letters are sorted without regard to case.
var animals = ["Zebra", "ape"]; function sortAscendingAlpha (element1, element2) { return (element2.toLowerCase( ) < element1.toLowerCase()); } animals.sort(sortAscendingAlpha); trace(animals); // Displays: "ape,Zebra"
Of course, the comparison does not always have to involve simple strings and numbers. Here we sort an array of movie clips in ascending order, according to their pixel area:
var clips = [square1, square2, square3]; function sortByClipArea (clip1, clip2) { clip1area = clip1._width * clip1._height; clip2area = clip2._width * clip2._height return clip1area - clip2area; } clips.sort(sortByClipArea);
That's a mighty fine sortin' machine!
Flash MX adds another useful sorting method not available in Flash 5. When the elements of an array are objects that share a specific property name, we can use sortOn( ) to sort the objects quickly according to that property. For details, see Array.sortOn( ) in the ActionScript Language Reference.
Something of a subset of splice( ), the slice( ) method retrieves a series of elements from an array. Unlike splice( ), slice( ) only retrieves elements. It creates a new array and does not affect the array on which it is invoked. The slice( ) method has the following syntax:
origArray.slice(startIndex, endIndex)
where startIndex specifies the first element to retrieve and endIndex specifies the element after the last element we want to retrieve. The slice( ) method returns a new array containing a copy of the series of elements from origArray[startIndex] to origArray[endIndex - 1]. If endIndex is omitted, it defaults to origArray.length, and the returned array contains the elements from origArray[startIndex] through origArray[origArray.length - 1]. Here are a couple of examples:
var myList = ["a", "b", "c", "d", "e", "f"]; myList.slice(1, 3); // Returns ["b", "c"], not ["b", "c", "d"] myList.slice(2); // Returns ["c", "d", "e", "f"]
We can use join( ) to produce a string that represents all the numbered elements of an array. The join( ) method starts by converting each element of the specified array to a string. Undefined elements are converted to the empty string (""). Then join( ) concatenates all the strings into one long string, separating them with a character (or series of characters) called a delimiter. Finally, join( ) returns the resulting string. The syntax of join( ) is:
arrayName.join(delimiter)
where delimiter is the string used to separate the converted elements of arrayName. If delimiter is unspecified, it defaults to a comma. The result of a join( ) statement is most easily understood through an example:
var siteSections = ["animation", "short films", "games"]; // Sets siteTitle to "animation>> short films>> games" var siteTitle = siteSections.join(">> "); // Sets siteTitle to "animation:short films:games" var siteTitle = siteSections.join(":");
Note that join( ) does not modify the array upon which it is invoked. Instead, it returns a string based on that array.
When called without a delimiter argument, join( ) behaves exactly like toString( ). Because toString( ) does not add a space after the comma it uses to delimit elements, we may wish to use join( ) with ", " as the delimiter if we want nicely formatted output:
var x = [1, 2, 3]; trace(x.join(", ")); // Displays: "1, 2, 3" instead of "1,2,3"
The join( ) method has a potentially surprising result when used on an array containing elements that are themselves arrays. Since join( ) converts elements to strings, and arrays are converted to strings via their toString( ) method, nested arrays are converted to strings using a comma delimiter, not the delimiter supplied as an argument to join( ). In other words, any delimiter supplied to join( ) doesn't affect nested arrays. For example:
var x = [1, [2, 3, 4], 5]; x.join("|"); // Returns "1|2,3,4|5" not "1|2|3|4|5"
Reader Exercise: Write your own custom function that uses the specified delimiter to join nested arrays. Hint: Loop through all the elements of the array, checking for those that are arrays themselves. Use the instanceof operator (described in Chapter 5) to check whether a datum is an array. Can you create a recursive version that will work with arrays nested to an arbitrary depth?
As we'll see in Chapter 12, toString( ) is a method, common to all objects, that returns a string representation of the object upon which it is invoked. In the case of an Array object, the toString( ) method returns a list of the array's elements, converted to strings and separated by commas. The toString( ) method can be called explicitly:
arrayName.toString()
Typically, we don't use toString( ) explicitly; rather, it is invoked automatically whenever arrayName is used in a string context. For example, when we write trace(arrayName), a list of comma-separated values appears in the Output window; trace(arrayName) is equivalent to trace(arrayName.toString( )). The toString( ) method is often helpful during debugging when we need a quick, unformatted look at the elements of an array. For example:
var sites = ["www.moock.org", "www.macromedia.com", "www.oreilly.com"]; // Display our array in a text field debugOutput_txt.text = "The sites array is " + sites.toString(); trace("The sites array is " + sites.toString()); // Explicit use of toString( ) trace("The sites array is " + sites); // Implicit use of toString( )
The join( ) method covered in the previous section offers greater formatting flexibility than toString( ).