E4X is a tidy little querying language. Of all the new features in AS3, this one is probably the best.
Most people use E4X for retrieving data straight up, which is good if you have your server-side code doing the filter crunching, but if you’re going for chunky data calls, it’s nice to be able to filter on the client-side too. Thankfully, E4X gives us plenty of capability for filtering.
Let’s start by laying out our common data set. I’m using an XML file with automotive data consisting of Makes->Make->Models->Model. You can see the XML file here: AutomotiveData.xml
In my code, I’ve set a variable called xmlData to the value of the data in AutomotiveData.xml. xmlData corresponds to the root node (Makes) in the data.
Basic E4X filters:
Let’s retrieve the list of Honda models:
trace(xmlData.Make.(@name == “Honda”).Models.toXMLString());
“name” is the attribute of the Make node that we’re working with, so you target attributes with the @ symbol in front. Everything inside of the parentheses is interpreted as normal ActionScript, so you can run pretty much any code that you want to inside there, even if it doesn’t make sense. Just remember, in order to actually filter data, you need to run a function that evaluates to a Boolean: true or false.
Multiple E4X filters:
Let’s get all Makes that have models that cost less than $30,000 and have at least 8 models (this will return Honda and Toyota).
trace(xmlData.Make.(@minMSRP < 30000 && @modelCount >= 8).Models.toXMLString());
Now, you’ll notice that I have an attribute called @modelCount that contains a sum of the model count for that Make. If you don’t have that attribute, you could also count quite easily:
trace(xmlData.Make.(@minMSRP < 30000 && Models.Model.length() >= 8).Models.toXMLString());
E4X recognizes Models as the currently scoped Models node, since we’ve already descended to xmlData.Make. It would not work to say xmlData.(Models.Model.length() >=
because the scope of “xmlData.” only gets you to Make, not Models.
In the above example, notice that I ran a function, .length() on the XML, and that I in fact ran E4X inside of E4X. You can nest E4X pretty much as far as you would ever need without hitting any limits. If you do hit limts, you should restructure your XML or consider using a filtering function, which I will talk about later.
Nested E4X and Local Variables
Let’s say we want to find all Makes that have a Model that starts with the letter “R”:
var strModelFirstCharacter:String = “R”;
trace(xmlData.Make.(Models.Model.(@name.toString().indexOf(strModelFirstCharacter) == 0).length() > 0).@name.toXMLString());
Notice that we’re running a nested query so that we can keep the Make scope and get the Make.@name attribute. Also, we’ve passed in a variable this time so that we can modify this E4X to run off of any character or phrase.
If a user was selecting the filters, you could also have them pass in a value, like so:
var strAttribute:String = “mpgCity”; //mpgHighway
var intMinValue:uint = 22;
trace(xmlData.Make.Models.Model.(attribute(strAttribute) > intMinValue).@name.toXMLString());
E4X Filter Functions
Something that just using variables above leaves out is the ability to change the greater than operator without a bunch of messy inline code. However, since we can do basically anything inside of the parentheses, you can create a function to handle variance:
function GenericFilter(data:XML, attributeName:String, value:Object, comparison:String = "equal"):Boolean {
switch(comparison) {
case "<":
case "less":
return data.attribute(attributeName) < value;
case ">":
case "greater":
return data.attribute(attributeName) > value;
case "=":
case "equal":
return data.attribute(attributeName) == value;
}
return false;
}
Then, your E4X can be structured as such:
var strAttribute:String = “seating”;
var intValue:Object = 8;
var comparisonType:String = “=”;
trace(xmlData.Make.Models.Model.(GenericFilter(valueOf(), strAttribute, intValue, comparisonType)).@name.toXMLString());
Notice the valueOf() function; this is passing Model nodes into the GenericFilter, which is why that function accepts an XML node as the data parameter. The comparisonType is simply a string that you can pass in. I’m using a switch because I don’t think there’s a way to pass an actual equality or greater than operator as a parameter in ActionScript. You could of course extend GenericFilter() to accept greater than or equal, or do fuzzy logic, whatever you want to do is acceptable.
These E4X filter functions are pretty much the most powerful thing in E4X; you can run anything that you could possibly want to.
More to come…
I’ll update this article with more information as requests come in or I try new things. Keep in mind that E4X attribute filters don’t work in switch statements.