Introduction to ActionScript 2.0/Arrays
Key concepts:
- Indexed arrays vs. associative arrays
- The Array constructor and array literals
- Nested arrays
- Array concatenation
- Adding and removing elements from an array
- Popping, pushing, shifting and unshifting
- Splicing
- Extracting array elements by slicing
- Inter-conversion between arrays and strings
- Array sorting
- Sorting numbers and strings
- Custom criteria
- Returning indices
- Sorting on fields
- Reversing arrays
We've learnt about arrays back in the control statements chapter. However, we didn't learn how we can manipulate them. That's our focus in this chapter.
What is the nature of the Array class?
editIn computer programming, there are actually two kinds of arrays, indexed arrays and associative arrays (also known as hashes). In indexed arrays, each array element has an index, whereas in associative arrays, each array element has a name. In ActionScript, the Array class produces indexed arrays, while the Object class produces associative arrays.
In the chapter about control statements, we've covered the use of Array class's constructor function without parameters, and we won't go through it again in this chapter. In this chapter, we will go through two more ways to use the constructor function, as well as the array literal. (Yes, arrays have literals too!)
Code | Result |
---|---|
var dogs:Array = new Array(10);
dogs[0] = "Spot";
dogs[1] = "Lucky";
dogs[2] = "Max";
...
dogs[9] = "Rover";
trace(dogs[0]);
var fruits:Array = new Array("Apples", "Bananas", "Oranges", "Pears");
trace (fruits[2]);
var months:Array = ["J", "F", "M", "A", "M", "J", "J", "A", "S", "O", "N", "D"];
trace(months[9]);
|
|
In the first example, we initialised the dogs array by specifying its length, then assigned a string value to each of the elements. In the second example, we put in the values directly into the constructor function.[1] In the third example, we used the array literal. The array literal is always surrounded by a pair of square brackets, and each element is separated by a comma, like this: [1,1,3,5,8,13,21,34].
What is a multidimensional array?
editA multidimensional array, or a nested array, is an array that contains arrays as elements. It can represent data in a tabular form. For instance:
Code | Result |
---|---|
var multiplicationTables:Array = new Array(10);
for(var i:Number = 0; i < multiplicationTables.length; i++){
multiplicationTables[i] = new Array(10);
for(var j:Number = 0; j < multiplicationTables[i].length; j++){
multiplicationTables[i][j] = i * j;
}
trace(multiplicationTables[i]);
}
|
|
Once we've learnt about dynamic MovieClip and TextField creation as well as the drawing API, we can actually present this array as a table.
Fortunately for us, there's a shortcut to creating nested arrays: using the array literal:
Code | Result |
---|---|
var someNestedArray:Array = new Array([1, 3, 5, 7, 9], [1, 1, 2, 3, 5], [2, 4, 6, 8, 10]);
trace(someNestedArray[0]);
trace(someNestedArray[1]);
trace(someNestedArray[2]);
trace(someNestedArray);
|
|
Note, in the last trace, that the multidimensional array is 'flattened' when it is turned into a string and traced. We can use the split method to create a really flattened array:
Code | Result |
---|---|
var someNestedArray:Array = new Array([1, 3, 5, 7, 9], [1, 1, 2, 3, 5], [2, 4, 6, 8, 10]);
var someFlattenedArray:Array = String(someNestedArray).split(",");
trace(someFlattenedArray.length);
|
|
As the length is 15, there are 15 elements rather than three elements with five elements each.
How can I concatenate two arrays together?
editConcatenating two arrays is a fairly simple operation. It requires the array1.concat(array2, array3...) method. This method does not flatten out nested arrays. The following code combines two arrays:
Code | Result |
---|---|
var array1:Array = new Array("a", "b", "c");
var array2:Array = new Array("d", 5, "f");
var array3:Array = array1.concat(array2);
trace(array3);
|
|
concat() should not be confused with join(), which will be covered later.
How can I add to and remove from an array?
editThere are several methods to add and remove elements from an array:
- array.pop()
- array.push(object1, object2...)
- array.shift()
- array.unshift(object1, object2...)
- array.splice(index of the first element, [number of elements,] [object1, object2...])
This is starting to get a bit intimidating, so let's go through each of them one by one.
Popping and pushing
editpop() removes the last element from the array and then returns the popped element. This example will show how the “pop” method works:
Code | Result |
---|---|
var myArray = new Array(1, 2, 3, 4);
var popped:Number = myArray.pop();
trace(popped);
trace(myArray);
|
|
Firstly, we declare the 'myArray' array with four elements: 1, 2, 3 and 4. Next, we declare the 'popped' variable. We then pop 'myArray' and assign the last element to the 'popped' variable. We then trace popped to see the element which was popped, then trace the new array.
push() adds one or more elements to the end of the array, and then returns the length of the new array:
Code | Result |
---|---|
var myArray = new Array(1, 2);
var newLength:Number = myArray.push(3, 4);
trace(newLength);
trace(myArray);
|
|
We first declare the 'myArray' array with two elements, 1 and 2. The second line pushes the values 3 and 4 into myArray (returning the new length), declares the 'newLength' variable, then assigns the new length to newLength.
Shifting and unshifting
editShifting and unshifting are similar to popping and pushing, but elements are added and removed from the beginning, not the end, of an array.
Here's an example of shifting:
Code | Result |
---|---|
var myArray = new Array(1, 2, 3, 4);
var shifted:Number = myArray.shift();
trace(shifted);
trace(myArray);
|
|
Firstly, we declare the 'myArray' array with four elements: 1, 2, 3 and 4. Next, we declare the 'shifted' variable. We then shift 'myArray' and assign the first element to the 'shifted' variable. We then trace shifted to see the element which was shifted, then trace the new array.
Here's an example of unshifting:
Code | Result |
---|---|
var myArray = new Array(3, 4);
var newLength:Number = myArray.unshift(1, 2);
trace(newLength);
trace(myArray);
|
|
We first declare the 'myArray' array with two elements, 3 and 4. The second line unshifts the values 1 and 2 into myArray (returning the new length), declares the 'newLength' variable, then assigns the new length to newLength.
Splicing
editSplicing an array is a more flexible and sophisticated process then pushing, popping, shifting and unshifting. (Slice, split, splice, shift... confused yet?) Splicing is the process of both removing and adding elements to an array. The splice() methods adds elements to and remove elements from the array, then returns the removed elements.
Let's look at the parameters again:
array.splice(index of the first element, [number of elements], [new element 1, new element 2...])
As you can see, only the first parameter is required. The first parameter indicates where you want to start doing your removing and adding work. It can be a normal index (0, 1, 2, 3...), or a negative one (-1 as the last element, -2, -3, -4...). If this is the only parameter you include, all the elements starting from that one will be removed:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2);
trace(myArray);
|
|
The second parameter is the number of elements to be removed. Let's alter the above example to remove only the 3:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 1);
trace(myArray);
|
|
The rest of the parameters are elements to be added to the array to replace the removed elements.
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 1, 6, 7, 8);
trace(myArray);
|
|
We can also set the second parameter to 0 so that we add elements without removing any:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4];
myArray.splice(2, 0, 6, 7, 8);
trace(myArray);
|
|
How can I extract elements from an array?
editslice() returns certain elements in an array. Here are the parameters of slice():
array.slice([index of the first element], [index of the last element]);
Both parameters are optional. If the both elements are left blank, the whole array is copied:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4];
var secondArray:Array = myArray.slice();
secondArray.pop();
trace(secondArray);
trace(myArray);
var thirdArray:Array = myArray;
thirdArray.pop();
trace(thirdArray);
trace(myArray);
|
|
Again, the whole array is copied, not linked. In the above example, changing secondArray does not change the original myArray as myArray was copied to secondArray. However, changing thirdArray does change the original myArray as thirdArray only contains a link to myArray, not a copy of myArray.
Let's try again, this time with the first parameter:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4, 5];
var slicedArray:Array = myArray.slice(2);
trace(slicedArray);
|
|
Only everything from the element 2 is copied to slicedArray. Now let's try that a third time, with both parameters:
Code | Result |
---|---|
var myArray:Array = [1, 2, 3, 4, 5];
var slicedArray:Array = myArray.slice(2, 3);
trace(slicedArray);
|
|
Only the elements from element 2 to element 3 are copied to slicedArray.
What are joining and splitting?
editSometimes, we want to split a string into many substrings separated by a delimiter (a symbol showing where we should split the string), then tuck each substring into an array. In this case, we need the split method:
Code | Result |
---|---|
trace("Foo + Bar + Baz + Qux".split(" + "));
trace("Foo + Bar + Baz + Qux".split(" + ", 3));
trace("Foobar".split(""));
|
|
In the first trace, we used " + " as a delimiter to create an array containing the elements Foo, Bar, Baz and Qux. In the second trace, we limited the number of substrings to 3. In the third, the delimiter was a blank string, so the each substring consisted of one character.
Joining an array is the reverse operation of splitting a string. It creates a new string using array elements and a certain delimiter (the first and only parameter):
Code | Result |
---|---|
trace(["Foo", "Bar", "Baz", "Qux"].join(" + "));
trace(["F", "o", "o", "b", "a", "r"].join(""));
trace(["Foo", "Bar", "Baz", "Qux"].join());
|
|
In the first trace, we joined the Foo, Bar, Baz and Qux into a string separated by ' + '. In the second, we got the word 'Foobar' as we used an empty string as our delimiter. In the third, we produced the same effect as trace(String(["Foo", "Bar", "Baz", "Qux"].join()));
since we did not specify any delimiter and the default comma was used.
How can I sort arrays?
editSorting is a very complicated topic in ActionScript. In this section, we will try to do it!
How can I sort with sort()?
editThe sort() function can sort with one criterion.
How can I sort strings with Unicode code points?
editThe easiest way to sort a bunch of strings is to use Unicode code points. (We discussed Unicode code points way back in the chapter about operators.) This does not require any parameters:
Code | Result |
---|---|
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort();
trace(myArray);
myArray = [1, 300, 2, 4, 6];
myArray.sort();
trace(myArray);
|
|
Note that Lucky and Max come before rover and spot because uppercase letters precede lowercase letters in Unicode. Also note that the numbers are also arranged according to their Unicode code points, not their magnitudes or values.
If we want the sort to ignore casing, we can pass an Array class constant, Array.CASEINSENSITIVE, into the sort() function:
Code | Result |
---|---|
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort(Array.CASEINSENSITIVE);
trace(myArray);
|
|
Array.CASEINSENSITIVE can be replaced by the number 1.
How can I sort numbers?
editAs we've seen above, sort() sorts elements as strings by default. To sort numbers, we need to pass another Array class constant, Array.NUMERIC, into the sort() function:
Code | Result |
---|---|
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC);
trace(myArray);
|
|
Notice that negative infinity is always the smallest and infintiy is always the largest. Array.NUMERIC can be replaced by the number 16.
How can I sort with my own criterion?
editSometimes, using Unicode code points just isn't enough. In this case, we have to make our own functions. The first parameter of the sort() function is a function with two parameters which decide which of two values should come first. The function should return 1 if the first argument comes before the second argument, -1 if the second argument comes before the first, and 0 if it doesn't matter. If that sounds complicated, look at this example:
Code | Result |
---|---|
var myArray:Array = ["a good dog called spot", "a canine called lucky",
"a cute mammal called Rover", "an adorable animal called Max"];
function whichComesFirst(a:String, b:String):Number{
var dog1:String = a.substring(a.lastIndexOf(" ")+1).toUpperCase();
var dog2:String = b.substring(b.lastIndexOf(" ")+1).toUpperCase();
if(dog1 > dog2){
return 1;
} else if(dog1 < dog2) {
return -1;
} else {
return 0;
}
}
myArray.sort(whichComesFirst);
trace(myArray);
|
|
In the whichComesFirst function, we first extracted the last word from the string. Then, we converted everything to uppercase before comparing. That way, the case is ignored. This function sorts the array in a truly alphabetical order, similar to what we've achieved with Array.CASEINSENSITIVE.
How can I sort in descending order?
editTo sort in descending order, we pass on the value Array.DESCENDING into the first parameter:
Code | Result |
---|---|
var myArray:Array = ["spot", "lucky", "Rover", "Max"];
myArray.sort(Array.DESCENDING);
trace(myArray);
|
|
This can also be applied to custom criteria (note that the comparison function is the first parameter and the array constant is the second parameter in this case):
Code | Result |
---|---|
var myArray:Array = ["a good dog called spot", "a canine called lucky",
"a cute mammal called Rover", "an adorable animal called Max"];
function whichComesFirst(a:String, b:String):Number{
var dog1:String = a.substring(a.lastIndexOf(" ")+1).toUpperCase();
var dog2:String = b.substring(b.lastIndexOf(" ")+1).toUpperCase();
if(dog1 > dog2){
return 1;
} else if(dog1 < dog2) {
return -1;
} else {
return 0;
}
}
myArray.sort(whichComesFirst, Array.DESCENDING);
trace(myArray);
|
|
If we've already got another array constant as a parameter, we should use the bitwise OR operator (|)[2] to separate the two array constants.
Code | Result |
---|---|
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC | Array.DESCENDING);
trace(myArray);
|
|
What if I don't want to modify the original array?
editThe array constant Array.RETURNINDEXEDARRAY will make sort() return an array containing the new index of each number. For example, if the original array is [3, -1, 4], sort() will return [1, 0, 2], where the 3 is the second number (index 1), -1 is the first number (index 0), and 4 is the third number (index 2) in the sorted array.
Code | Result |
---|---|
var myArray:Array = [1, -3, 5, 7, 3];
var myIndices:Array = myArray.sort(Array.NUMERIC | Array.RETURNINDEXEDARRAY);
trace(myIndices);
var mySortedArray:Array = new Array();
for(i = 0; i < myIndices.length; i++){
mySortedArray[i] = myArray[myIndices[i]];
}
trace(mySortedArray);
|
|
Note that the terms are slightly different in databases. In databases, myIndices is known as an index, while our array indices are known as index keys. In this book, to avoid confusion, we will avoid using these terms.
What if I don't want repeated items?
editIf you use the Array.UNIQUESORT constant, Flash will return 0 instead of modifying the array if two or more elements are found to be equivalent. We'll use this constant in the fourth section, when we learn about exception handling.
How can I sort arrays with sortOn()?
editSometimes, we want to sort hashes or objects inside arrays, and it would be cumbersome to use custom criteria with sort(). That's where sortOn() comes in.
sortOn treats arrays like tables in a database. In a database, a table is a collection of entities, or records, with the same record structure. Each entity has several fields, each of which stores a particular attribute of the record. Sometimes, we need to use a query to sort the fields in a particular way. Sorting keys are the fields which are used to sort. The primary sort key is first used to sort the data. When there are two or more data with the same attribute in a certain field, the secondary sort key is used to sort those data. If there are still equivalent values, the tertiary sort key is used, and so on.
Let's say we have a table containing data on several books. We want to arrange the book titles in descending order first. If there are two books with the same title, then we sort by the author's last name in ascending order, then the author's first name in ascending order, and finally the publication year in descending order. In SQL, the syntax for doing this is:
SELECT *
FROM Books
ORDER BY Title DESC, Author_last ASC, Author_first ASC, Pub_year DESC;
Even if you have no knowledge of SQL, you can probably understand some of the code. Unfortunately, it's more complicated when it comes to ActionScript. Let's try to rewrite this SQL into ActionScript:
Code | Result |
---|---|
var books:Array = new Array({title:"The Adventures of Teddy Bear", last:"Bloggs", first:"Joe", year:1996},
{title:"Journey to the West", last:"Wu", first:"Cheng'en", year:1542},
{title:"The Adventures of Teddy Bear", last:"Bloggs", first:"Fred", year:2012});
books.sortOn(["title", "last", "first", "year"],
[Array.CASEINSENSITIVE | Array.DESCENDING, Array.CASEINSENSITIVE,
Array.CASEINSENSITIVE, Array.NUMERIC | Array.DESCENDING]);
for(var i:String in books){
trace(books[i].title + " by " + books[i].first + " "
+ books[i].last + " published in " + books[i].year);
}
|
|
That looks complicated! Let's break it down, one by one.
- Firstly, we created a new Array with three object literals. Each object has a title, a last name, a first name and a year. Note that each object here is an associative array.
- Next, we used sortOn. The first parameter of sortOn is an array containing the property names to be used as sort keys. The 0th element, title, is the primary sorting key, the 1th element, last, is the secondary sorting key, and so on.
- The second parameter is an array containing the array constants to be used for each sort. In this case, we want Array.CASEINSENSITIVE for the string fields, Array.NUMERIC for the publication year, and Array.DESCENDING the author and publication year. The bitwise OR operator is also used.
- Finally, we traced the new array from the 0th element to the 2nd.
It's not so difficult once we break it down, is it? We can also use it to sort instances of MovieClips and other classes. This seemingly unuseful technique comes in handy when you have a lot of elements to be presented. For example, if you're making a game with a level editor, you want a way to display the levels. The player can sort the levels by the date of publication, rating, difficulty, etc.
How can I reverse an array?
editreverse() is a quick and easy way to reverse arrays.
Code | Result |
---|---|
var myArray:Array = [1, 300, -Infinity, 2, 4, 4.3e-3, Infinity, -3, 6, 0x2A];
myArray.sort(Array.NUMERIC | Array.DESCENDING);
trace(myArray);
myArray.reverse();
trace(myArray);
|
|
Conclusion
editWe've gone through two chapters in the second section, and so far, we've only been manipulating data without any real output on the screen. That might not be too satisfying for you, so let's move on to the very heart and soul of the Flash application: the MovieClip.