Author Archive: Robert Berry

Sorting an array without sorting it

Have you ever wanted to access an array in a different sequence without
changing its current sequence? The ActionScript 3.0 constant, Array.RETURNINDEXEDARRAY, allows you to do that. When you call Array.sort() with RETURNINDEXEDARRAY as a parameter, sort() returns an array of indexes in sorted order. You can then use the indexed array to access the primary array in that sequence. The following example contains an array of students’ names: John, Rachel, Melissa, Calvin. Let’s say this sequence represents their order for the seating chart. Now, however, we would
like to get them in alphabetical order for the grade book. We can do this by calling students.sort(Array.RETURNINDEXEDARRAY) and using the indexed array that it returns. Note that by default, sort() sorts in ascending order. Here’s the code:

var students:Array = ["John", "Rachel", "Melissa", "Calvin"];
var index:Array = students.sort(Array.RETURNINDEXEDARRAY);
for(var i:int = 0; i < students.length; i++) {
trace(students[index[i]] + ": student index: "
+ index[i] + " sorted: " + i);
}

The example uses a for() loop to step through the returned index array in order, using the variable i, initialized to 0. The trace() statement displays the following information: the student’s name (students[index[i]]); the position of that student in the original array (the value of index[i]); the sorted position of the student, which is the sequence of index, or the value of i. The output looks like this:

Calvin: student index: 3 sorted: 0
John: student index: 0 sorted: 1
Melissa: student index: 2 sorted: 2
Rachel: student index: 1 sorted: 3

And the order of the students array remains unchanged: John, Rachel, Melissa, Calvin.

You can obtain additional sequences by using the OR operator| to combine
RETURNINDEXEDARRAY with other sort() constants. For example, the call to students.sort() in the following code combines RETURNINDEXEDARRAY with the constant DESCENDING, to access students in descending order:

var students:Array = ["John", "Rachel", "Melissa", "Calvin"];
var index:Array = students.sort(Array.RETURNINDEXEDARRAY | Array.DESCENDING);
for(var i:int = 0; i < students.length; i++) {
trace(students[index[i]] + ": student index: "
+ index[i] + " sorted: " + i);
}

The output for this version looks like this:

Rachel: student index: 1 sorted: 0
Melissa: student index: 2 sorted: 1
John: student index: 0 sorted: 2
Calvin: student index: 3 sorted: 3

For more information on the Array class, see:
http://help.adobe.com/en_US/AS3LCR/Flash_10.0/Array.html

Update on the Adobe TextLayout Framework

The TextLayout Framework API has had quite a few changes since it was released on Adobe Labs last November. And the documentation is being updated, as well, to reflect these changes. While these changes are still in progress, the TextLayout Framework overview has been updated and incorporated into the “Working with Text” chapter of Programming ActionScript 3.0. You can find instructions on how to access and review a PDF of that chapter on Francis Cheng’s blog.

The API reference is availabe as part of the Flex Gumbo API Documentation, which accepts comments. Look for the flashx.textLayout.* packages

We’d love to get any feedback you might have.

If you’d like to see what people are doing with the TextLayout Framework, check out the New York Times Reader 2.0 application, which is built with Adobe AIR.

DataProvider.getItemIndex()

A user recently asked why the call to DataProvider.getItemIndex() in the following example returns -1:

import fl.controls.List;
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
dp.addItem({label:"AL", data:"Montgomery"});
dp.addItem({label:"AK", data:"Juneau"});
dp.addItem({label:"AR", data:"Little Rock"});
// assign the data provider to the list
var myList:List = new List();
myList.dataProvider = dp;
myList.rowHeight = 35;
myList.rowCount = dp.length;
myList.move(10, 10);
addChild(myList);	// display the list
// this attempt to locate an item in the list returns -1
trace("Index is: " + myList.dataProvider.getItemIndex({label:"AK", data:"Juneau"}));

Paul Robertson explains:

The problem is that the getItemIndex() method matches exact object references, not objects with matching properties. The first object, {label:”AK”, data”Juneau”}, may
have identical property values with the one that’s being passed to the getItemIndex() method, but they’re not exactly the same object – they’re just two different objects that happen to have the same property values.

To get a match with getItemIndex(), you need to pass a reference to the actual object that’s in the data provider. To do that, of course, you need a reference to the
same object, for example in a variable. Here’s a variation of your code that works:

import fl.controls.List;
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
var item1:Object = {label:"AL", data:"Montgomery"};
var item2:Object = {label:"AK", data:"Juneau"};
var item3:Object = {label:"AR", data:"Little Rock"};
dp.addItem(item1);
dp.addItem(item2);
dp.addItem(item3);
var myList:List = new List();
myList.dataProvider = dp;
myList.rowHeight = 35;
myList.rowCount = dp.length;
myList.move(10, 10);
addChild(myList);
trace("Index is: " + myList.dataProvider.getItemIndex(item2));

Of course, you are probably really trying to do some sort of search to find the index of an item that matches a user entered string. The only way I can see to do that is to loop over the data provider one item at a time and compare the property values. You can either loop over the data provider directly using dp.length and
dp.getItemAt(index), or else keep the objects in a separate array. This code demonstrates the first approach:

import fl.controls.List;
import fl.controls.Button;
import fl.controls.TextInput;
import fl.data.DataProvider;
var dp:DataProvider = new DataProvider();
dp.addItem({label:"AL", data:"Montgomery"});
dp.addItem({label:"AK", data:"Juneau"});
dp.addItem({label:"AR", data:"Little Rock"});
var myList:List = new List();
myList.dataProvider = dp;
myList.rowHeight = 35;
myList.rowCount = dp.length;
myList.move(10, 35);
addChild(myList);
var ti:TextInput = new TextInput();
ti.width = 100;
ti.move(10, 10);
addChild(ti);
var btn:Button = new Button();
btn.width = 50;
btn.label = "Search";
btn.move(120, 10);
addChild(btn);
btn.addEventListener(MouseEvent.CLICK, btn_click);
function btn_click(event:Event):void
{
for (var i:int = 0, len:int = dp.length; i < len; i++)
{
if (dp.getItemAt(i).data == ti.text)
{
trace("Index is: " + i.toString());
return;
}
}
trace("Item not found");
}