« DataGrid.headerRelease example | Main | Happy holidays game... »

Sorting and the DataGrid component

Another Tuesday LiveDocs day! The following example continues the theme of DataGrids and sorting.

The following example sorts columns either numerically or alphabetically. By default columns are sorted alphanumerically, so if you had a DataGrid with "1, 213, 11, 7" it would sort as "1,11,213,7". By writing a custom sorting function that uses Array.sortOn, you can sort the dataProvider yourself using a numeric or text sort (even sort text as case insensitive).

1. Make sure you have DataGrid in Library
2. Add following ActionScript to frame 1 of the Timeline:

import mx.controls.gridclasses.DataGridColumn;
var my_dp:Array = new Array({name:'Grissom, M.', avg:0.279}, {name:'Bonds, B.', avg:0.362}, {name:'Cruz, D.', avg:0.292}, {name:'Snow, J.', avg:0.327});
var dg = this.createClassObject(mx.controls.DataGrid, "my_dg", 999, {dataProvider:my_dp});
dg.setSize(240, 180);
var name_dgc:DataGridColumn = new DataGridColumn("name");
name_dgc.headerText = "Name:";
name_dgc.width = 160;
dg.addColumn(name_dgc);
var avg_dgc:DataGridColumn = new DataGridColumn("avg");
avg_dgc.headerText = "Avg:";
avg_dgc.width = 60;
dg.addColumn(avg_dgc);
//
var myListener:Object = new Object();
myListener.headerRelease = function(evt:Object) {
var sortOrder:String = evt.target.sortDirection;
var sortColumn:String = evt.target.columnNames[evt.columnIndex];
switch (sortColumn) {
case 'name' :
sortArray(my_dp, sortColumn, "TEXTNOCASE", sortOrder);
break;
case 'avg' :
sortArray(my_dp, sortColumn, "NUMERIC", sortOrder);
break;
}
};
my_dg.addEventListener("headerRelease", myListener);
//
function sortArray(my_array:Array, sortColumn:String, sortType:String, sortOrder:String) {
var sortOptions:Number = 0;
switch (sortType.toUpperCase()) {
case 'NUMERIC' :
sortOptions |= Array.NUMERIC;
break;
case 'TEXTNOCASE' :
sortOptions |= Array.CASEINSENSITIVE;
break;
}
if (sortOrder.toUpperCase() == 'DESC') {
sortOptions |= Array.DESCENDING;
}
my_array.sortOn(sortColumn, sortOptions);
}

3. Format the code (press the Auto Format button).
4. Test.

Comments

Hey Jen,

How do you preselect a row in a Datagrid, from previously entered data or from another object?

That wasn't very clear.. I am sorry.

What I meant was that I have some data in a datagrid that I want to have one of the rows preselected.

Wait.. Maybe a drop-down combo box would be better..

Thanks.
Micah

Hi Micah,

Try this:

my_dg.dataProvider = [{num:1}, {num:213}, {num:11}, {num:7}];
my_dg.selectedIndex = 1;


This code preselects the second row in the DataGrid (0 based, so the second row).If you want to pre-select multiple rows, you have to make sure that the multipleSelection property is set to true first.

my_dg.dataProvider = [{num:1}, {num:213}, {num:11}, {num:7}];
my_dg.multipleSelection = true;
my_dg.selectedIndices = [1, 2];

Hope that helps,
Jen.

How about preselecting based on one of the VALUES in that list. I am having trouble getting that accomplished.

Thanks mucho appreociated.

Micah

Sure -- the following selects rows based on a value in the DataGrid.

my_dg.dataProvider = [{num:1}, {num:210}, {num:11}, {num:7}];
my_dg.multipleSelection = true;
my_dg.selectedIndices = selectOddNumbers();
function selectOddNumbers():Array {
var temp_array:Array = my_dg.dataProvider;
var return_array:Array = new Array();
for (var i = 0; i

Nice example! That sortDirection property really should have been public to start with. It's worth pointing out that this example makes the correct choice in using Array.sortOn rather than Array.sort (and a comparison function) - Array.sortOn is an order of magnitude faster than using that comparison function. It's kind of why we didn't offer a dGcolumn.sortFunction property per column, to avoid people trying. It would have maybe made more sense to add a sortOptions property for the column instead. Oh well.

I really wish that there was a way to easily define whether a column is text or numeric. I figure there has to be a simpler way to sort columns than the method listed above (no offense, jan).

Or if not, any chance that somebody at MM can add that in the next version? (crosses fingers)

I guess the sort by number will work for a date field as well in that a date is really a number.
Correct?

Hi jen, maybe this is not the right place to write this but...about your Data for Designers: Connecting to Data in Flash Using Data Wizards: I worked with your samples it works great..however when I put the datagrid component and the xml connector in a movie I can not see my data from the xml?

I hope you can answer me

Hi Jen - I tried your sorting code with my dataprovider (created from an array) and datagrid, but the columns still sort incorrectly, ie 78 comes after 120 etc

help!

regards

frank

Hi again - your code works fine; but this is my code: function fillMebersGrid(records:String):Void {
var tempRecords:Array = new Array();
tempRecords = records.split(rowDelimiter);
for (var i = 0; i var tempArr:Array = new Array();
var tempObj:Object = new Object();
tempArr = tempRecords[i].split(fieldDelimiter);
for (var j = 0; j tempObj[tableFields[j]] = tempArr[j];
}
myDataprovider.push(tempObj);
}
members_dg.dataProvider = myDataprovider;
}

myDataprovider is still an array here, so in theory your code should work with mine....

any thoughts or suggestions?

cheers

frank

1 month on so not sure if anyone cares, i had the same problem is frank above. The functions (correct values passed etc) were working but the grid still wasnt being sorted properley.
Im thinking the problem was related to the way the grid is being dynamically populated.
I changed:
my_dp.push(rowObj);
to:
my_dp.addItem(rowObj);

And any column that was numeric i used the Number() function when adding to the rowObj.
Quirky little solution but it worked!

I added a line to make the datagrid ediable:
dg.editable = true;

After that, the sorting doesn't work right anymore. Numbers are sorted like strings.

How can I contact jdehaan?

Alfred, where did you put that line of code? I used the code directly from this page and set the "dg.editable = true;" on line 5 (but anything after line 3 should work) and it worked like a charm.

I place it below line 5. Even now that I've moved it to line 5, it doesn't work once I type my own values.

I'm using the FIRST example code on top, and nothing else.

Try this:
Avg:
520
5100
101
12

The sorting isn't right. Can you please check?

Running on FlashMX 2004 ver 7.2

Alfred, yeah, that seems very weird. The batting averages are numbers, yet, after they've been edited, they seemingly change to string datatypes (which I guess makes sense, but is still buggy since you'd expect a numeric sort to try to convert it back to numbers).

I added this code to the sortArray function (right above the my_array.sortOn(...) bit):

var i:Number;
for (i=0 ;i<my_array.length; i++ ) {
   trace(i+": "+typeof(my_array[i][sortColumn]));
}

If you test your Flash document in the authoring environment, you should see 0..3 are all numbers. Now, change 101 to 1010 and it should be a mix of numbers and strings. I'm guessing you'll have to modify the code to convert from strings back to numbers. Not sure of the most efficient way, but seemingly the following code works:

function sortArray(my_array:Array, sortColumn:String, sortType:String, sortOrder:String) {
  var sortOptions:Number = 0;
  switch (sortType.toUpperCase()) {
  case 'NUMERIC' :
    var i:Number;
    for (i=0; i<my_array.length; i++) {
      my_array[i][sortColumn] = Number(my_array[i][sortColumn]);
    }
    sortOptions |= Array.NUMERIC;
    break;
  case 'TEXTNOCASE' :
...

Actually, this is another cool little trick (and probably a lot more efficient than my attempt at re-casting every row of the DataGrid to the Number data type.

You could create an event listener for the "cellEdit" event, which just casts the changed cell from the String back to a Number.

Code ahoy! Add this code around line 15 or something, or anywhere around the headerRelease event listener code:

// ----------
myListener.cellEdit = function(evt:Object) {
   var columnName:String = evt.target.columnNames[evt.columnIndex];
   var cell:Object = evt.target.dataProvider[evt.itemIndex];
   switch (columnName.toLowerCase()) {
     case 'avg':
     cell[columnName] = Number(cell[columnName]);
     break;
   }
}
my_dg.addEventListener("cellEdit", myListener);
// ----------

HTH
peter

Thanks Peter. Working fine now. Big THANKS!!!

I can't believe how hard it is to make numbers sort correctly....spent days figuring out and searching for answers. This tutorial is very possibly the ONLY number sorting tutorial on the INTERNET that actually works!

Peter, is it possible for u to send me a e-mail?
I'm actually trying to make a simple scoreboard that sorts score from highest to lowest... still have a bit more to do.... my first attempt at DataGrids/Arrays.
If you can help, I'll explain what I need. Should be very easy for you.

Thanks
Alf

Some things I can't figure out:

1) How can I make a button sort the avg cloume (always descening?) Instead of using the "headerRelease".

2) How to get a list of all the datagrid entries?
eg. trace(my_array.join()); shows me [object Object],[object Object],....

Thanks

Thanks!

Was having headaches trying to cast my to numbers and sorting by this did the trick.. thanks for sharing!

What do I need to do to create a primary and secondary sort key? e.g. first sort on Country then within that sort on Age

all your codes sort the arrays, alryt. do it in this way - each column taking data from different arrays (or an XML) and the numeric columns need to be sorted. if i put your code, i will get the arrays sorted, but the datagrid is not updated. by tracing we can c the arrays are sorted numerically. its not reflected in the datagrid. is there any function to update datagrid to be called? plz do help..

thank you.

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)