Posted by: Mat | June 1, 2007

Sorting XML-based Multi-Dimensional Data in ActionScript

A question came up today about sorting matrices of data in ActionScript.  The resolution was rather simple but I’ve heard through the grapevine that [students] might have some utility for this, so here it is.  And since we’re at it, let’s also add in the idea that the data is being fed from an external XML document.

Here’s the scenario that we faced.  iStreamPlanet produces a software application that is run during live poker tournaments which places an overlay on top of the encoded video similar to poker tournaments you might see on TV.  The actual system uses an interface built in C++ that writes an XML file as it is updated, which in turn is read by a Flash component layer and then drawn into the video card (the result is something that looks like the image below).

WSOP Tournament at Caesar’s Palace broadcast live by iStreamPlanet

For those of you who have been through any of the scripting classes, by now you should recognize that this needs a multi-dimensional array to work best.  Don’t be scared off by multi-dimensional arrays – think of them as just Excel spreadsheets.  So if you have a spreadsheet that looks something like this:

  A B C D
1 Hat Cat Rat Sat
2 Bar Car War Par

Then the your array would have been written as:

var myArray = new Array();
myArray[0][0] = "Hat";
myArray[0][1] = "Cat";
myArray[0][2] = "Rat";
myArray[0][3] = "Sat";
myArray[1][0] = "Bar";
myArray[1][1] = "Car";
myArray[1][2] = "War";
myArray[1][3] = "Par";

Download the materials here: Multi-Dimensional Array Sorting from XML Data in ActionScript (ZIP)

In the course of adding components to the application, we needed to output the current chip counts of each player and present them in order of most to least chips.  Of course, our XML ordering was based on the C# output which was not sorted and also contained string data where we needed to sort numerically.

<?xml version="1.0" encoding="ISO-8859-1"?>
<PlayersData>
  <Title>President Poker</Title>
  <GameOver>FALSE</GameOver>
  <DisplayMode>0</DisplayMode>
  <PlayersChipCount>
    <Player ID="1" Name="George Bush">$454</Player>
    <Player ID="2" Name="George Washington">$545,445</Player>
    <Player ID="3" Name="Ronald Reagan">$454,545</Player>
    <Player ID="4" Name="Bill Clinton">$54,545</Player>
    <Player ID="5" Name="John F. Kennedy">$787</Player>
    <Player ID="6" Name="Richard Nixon">$4,554</Player>
    <Player ID="7" Name="Theodore Roosevelt">$15,616</Player>
    <Player ID="8" Name="Abraham Lincoln">$564,564</Player>
    <Player ID="9" Name="James Madison">$5,451</Player>
  </PlayersChipCount>
  <!-- data removed here -->

<PlayersData>

First, we read the XML.  To do this you’ll need to open a socket:

xmlData = new XML();
xmlData.ignoreWhite = true;
xmlData.load("Data.xml");

Next, we created a function that broke up the data and put it into the array.

xmlData.onLoad = readCart;

function readCart(success){
  if (!success){
    trace("Error: CANNOT LOAD XML DATA");
    break;
  }else{
    trace("XML read successfully");
    for (i = 0; i < this.childNodes.length; i++){
      if (this.childNodes[i].nodeValue == null && this.childNodes[i].nodeName == "PlayersData"){
        topLevel = this.childNodes[i];
      }
    }
  }//function continues below

At this point we need to consider how to sort the data.  Remember that our original spec required that the output be sorted in descending chip count order.  But notice that the XML data that has symbols in it.  If you know a bit about programming, you’ll know that sorting is done based on the operating system preference.  In this case using a normal sort() function, Player 7 (Roosevelt) with $15,616 chips would be first…

Unparsed Sort Correct Sort
Roosevelt $15,616 Lincoln $564,564
Bush $454 Washington $545,445
Reagan $454,545 Reagan $454,545
Nixon $4,554 Clinton $54,545
Madison $5,451 Roosevelt $15,616
Washington $545,445 Madison $5,451
Clinton $54,545 Nixon $4,554
Lincoln $564,564 Kennedy $787
Kennedy $787 Bush $454

While ActionScript does have conversion capabilities, the problem in this scenario is that the symbols will not allow conversion to a numeric.  So let’s create a function that will “convert” the formatted field into a number.

function replace(origStr,searchStr) {
  var tempStr = "";
  var startIndex = 0;
  for (c = 0;c < origStr.length;c++) {
    thischar = origStr.substr(c,1);
    if ( thischar != searchStr ) {
      tempStr += thischar;
    }
  }
  return tempStr;
}

Using the conversion function (and note, you can use this function – albeit very simplistic – to do pretty much any type of string replacement), now we can finish off the original function.

  //create empty array container
  var dat:Array = Array();  //loop through array data
  for (i = 0; i <= 4; i++){
    //create empty array for internal data
    var dat2:Array = Array();
    //extract node data
    var playID   = topLevel.childNodes[3].childNodes[i].attributes.ID;
    var playName  = topLevel.childNodes[3].childNodes[i].attributes.Name;
    var playChip  = topLevel.childNodes[3].childNodes[i].childNodes[0];

    //set data into internal array
    //not sequence of functions (inside out):
    // 1. set playChip to a string - node data is being held as an object
    // 2. replace $ with a blank (see function)
    // 3. replace , with a blank (see function)
    // 4. set value as a numeric
    dat2[0]   = Number(replace(replace(playChip.toString(),"$"),","));
    dat2[1]   = playID;
    dat2[2]   = playName;
    dat2[3]   = playChip;

    //set internal array into external array
    //this creates a multi-dimensional array
    dat[i] = dat2;
  }
  //sort on index 0 with Numeric descending
  dat.sortOn([0],2|16);//trace output
  for (i = 0; i <= 4; i++) {
    trace(i+". "+dat[i][3]+" "+dat[i][2]+" at "+dat[i][4]+" each");
  }
}

Now test the movie (Ctrl+Enter) and check out the chip counts.

Incidentally, if you’re a poker buff, check out how this actually looks on screen by watching the World Series of Poker at http://www.worldseriesofpoker.com/


Leave a response

You must be logged in to post a comment.

Categories