MediaTemple Hosting

FlashDen - Your Choice for Flash Components and Effects

Bowler Hat Games - Deliriously Delightful

Be a Sponsor

Ways to use E4X to filter data in ActionScript 3

by Josh Tynjala

ActionScript 3 in Flash 9 includes powerful support for reading and manipulating XML. It's called E4X, and it gives us developers some useful new syntax. Read on for a super quick introduction to E4X followed by some powerful ways to filter your data using this feature.

E4X is powered by the new XML and XMLList classes. The most common way to declare a variable of one of these types is to pass it and XML string. However, many developers might miss the fact that you can declare XML directly within your classes or ActionScript code. It's supported natively by the compiler.

Actionscript:
  1. var data:XML =
  2.     <items>
  3.         <item name="Wii">
  4.             <source>Amazon</source>
  5.             <price>364.00</price>
  6.         </item>
  7.         <item name="Wii">
  8.             <source>Target</source>
  9.             <price>249.99</price>
  10.         </item>
  11.         <item name="X-Box 360">
  12.             <source>Amazon</source>
  13.             <price>399.99</price>
  14.         </item>
  15.         <item name="PlayStation 3">
  16.             <source>Amazon</source>
  17.             <price>599.99</price>
  18.         </item>
  19.     </items>;

E4X lets you drill down into the data to get the information you need without any excessive looping or strange calls to parent and child nodes. The following example gets a list of all the item names defined in the XML from the previous example. Notice that the name attribute is specified with the "@" symbol at the beginning.

Actionscript:
  1. var itemNames:XMLList = data.item.@name;

To take it a step further, you can filter the same set of data to find items with "Amazon" defined as the source value. Place a statement within the parentheses to check for a specific value. In this case, we check to see if an item's source is equal to a string value.

Actionscript:
  1. var amazonItems:XMLList = data.item.(source == "Amazon");

Interestingly enough, you can place just about any ActionScript statement within the parentheses. In the following example, I've included a trace statement to display each item's name in the console. I find this particularly useful for debugging.

Actionscript:
  1. data.item.(trace(@name));

You can filter by multiple fields as well. This example filters items from Amazon with a price under $400.00.

Actionscript:
  1. var items:XMLList = data.item.(source == "Amazon" && price <400);

Let's take it a step further. Say that your application filters items by source, like in the example where we only wanted items from Amazon. However, the filtering is controlled by the user through a ComboBox or another user interface component. E4X will let you compare attributes or children against a variable as well!

Actionscript:
  1. var sourceName:String = "Target";
  2. var itemNames:XMLList = data.item.(source == sourceName);

Please note that you must be sure the variable name is different than the name of the child. If I had named my variable source, instead of sourceName, the statement within the parentheses would never access the item's source value because it would always use the variable. In exact terms, (source == source) most definitely won't work!

Let's make things a little more interesting. What happens if the name of the value by which we're filtering should be dynamic as well? In the previous examples, we've accessed attributes directly, but you can call functions on the XML object too. Here, we call the attribute() to get an attribute's value by its name. This will also work for the child function.

Actionscript:
  1. var filterBy:String = "name";
  2. var filterValue:String = "Wii";
  3. var items:XMLList = data.item.(attribute(filterBy) == filterValue);

Finally, let's finish with something a little complex. Say the application's interface allows the user to filter across multiple fields, but each field is optional. We can do that too, but not directly through E4X (as far as I know; please prove me wrong!). This example shows how to filter by items from Amazon that cost exactly $599.99, but the fields Array could contain any number of items with additional items to filter by.

Actionscript:
  1. var filtered:XMLList = data.item;
  2. var fields:Array = [{name: "source", value: "Amazon"}, {name: "price", value: "599.99"}];
  3.        
  4. var fieldCount:int = fields.length;
  5. for(var i:int = 0; i <fieldCount; i++)
  6. {
  7.     var fieldName:String = fields[i].name;
  8.     var fieldValue:String = fields[i].value;
  9.     filtered = filtered.(child(fieldName) == fieldValue);
  10. }

I'm sure you can think of interesting ways to expand on that last example to create some very powerful filtering systems. In this particular case, I've only checked for equality, but with some simple changes to the logic, you could check for prices greater than or less than a certain value, or you could even search for substrings within a value. For instance, you might want to search for "PlayStation", and include results for PS2 and PS3 systems. E4X is very, very powerful.

Update: I've written two followup articles:

Like what you just read? Follow @joshtynjala on Twitter.

49 Comments

Dustin Senos

Wow. Very slick. I can’t wait to give that a go. Thanks for the heads up.

Xrum! Blog… » Blog Archive » Google and Flex

[...] E4X and Flex – This particular articles goes into explaining way XMLList and XML variables can be monipulated. [...]

More XML filtering with E4X in ActionScript 3 » Zeus Labs

[...] you haven’t read my first post about filtering data with E4X, go check it out now. Today, I’ll expand on it to add a few more options that you have at your [...]

Kris

Thanks for posting this, using variables as node name & value filters was exactly what I was just trying to do. You explained it very nicely, thanks again.

(Better to comment late than never!)

Steve

Excellent piece, a huge help. A question: are there any filters that allow filtering by START of a string, as in all books whose authorName starts with “C”?

Thanks.

Josh Tynjala

Steve, you can use any standard string functions that are available. I imagine substr() or indexOf() would be a good start.

reintroducing.com Blogging Receptacle » Blog Archive » E4X Articles

[...] Labs posted two great articles on starting out with E4X in AS3.  The first is how to filter data using E4X and the second is a follow up with more advanced techniques.  Really great stuff.  XML is [...]

Devon O.

I realize this excellent blog entry’s a bit old, but I just ran across it and thought I’d point out you can filter on multiple fields directly with e4x along these lines (like if you wanted a Wii under 300 bucks, for example):

var sortItem:String = "Wii";
var maxPrice:Number = 300;
var filteredList:XMLList = data.item.(@name == sortItem && price < maxPrice);
Josh Tynjala

Devon, I filtered two fields just like that in the example above where I wanted items from Amazon for less than $400. I assume you’re responding to my last example, where I use a loop to handle multiple filters. That example is for the case where you don’t know how many different fields you’re filtering by until runtime. The user might choose the specific filters needed similar to how smart playlists work in iTunes. There could be two, three, or any number of filters. In that case, it’s impossible to use straight E4X because you don’t know the exact filters needed until the user picks them.

Duane Hardy

In flex, I am using remoteobject call to a coldfusion cfc to return complex xml from a yahoo API. To use the above described method in actionscript do I have to declare “E4X” as my return type anywhere (like the httpservice) or can I just continue to return the xml from the cfc?

Josh Tynjala

Duane,

I can’t give you a definitive answer on that one, as I don’t work with RemoteObjects or CF. If your current returned data is an instance of the XML class (which is what is used by E4X), then you should be fine. If it’s a String as far as ActionScript is concerned, then you’re probably going to have to set a result format or do the conversion manually.

Duane Hardy

It is being returned as XML. I’ll give it a try and let you know how it turns out.

Metal Hurlant

Neat. That’s going to sound funny, but I hadn’t realized one could filter on sometimes missing attributes within E4X:

xmlList.(@attr == "2") will throw an exception if any of the children of xmlList doesn’t have an attribute “attr” present.
However, xmlList.(attribute("attr")==2") works just fine.
I wish I had thought of that 6 months ago.

Also, your last example can be rewritten as

var filtered:XMLList = data.item;
var fields:Array = [{name: "source", value: "Amazon"}, {name: "price", value: "599.99"}];
filtered = filtered.(fields.every(function(item:*,index:int,array:Array):Boolean {
    return child(item.name) == item.value;
}));

Technically, it’s one E4X expression, although it doesn’t exactly improve the code maintainability.

Nick

How can i filter an XML document to give me only one value if there are values that are the same?

Basicly.
I am pulling a XML document with a and then enabling the dates in my datechooser by creating disabledranges. My script can only handle one date value of 12/12/2007 but i have 12 dates that equal 12/12/2007.

Ric Moore

Your last example, filtering when you don’t know if an attribute exists – here’s the E4X solution:

var filtered:XMLList = data..item.(
    hasOwnProperty("@source") &&
    @source == "Amazon" &&
    hasOwnProperty("@price") &&
    @price == "599.99");

From Essential Actionscript 3.0:

To filter a list where not every item has a given attribute or child element, we must use hasOwnProperty( ) to check for the existence of that attribute or child before filtering on it. Otherwise, a reference error occurs. For example, the following code returns every element in someDocument that has a color attribute set to “red”:

someDocument..*.(hasOwnProperty("@color") && @color == "red")
Josh Tynjala

Actually Ric, in the last example, it’s meant to show how a user might choose to filter some fields, but not others. In that case, I assume that all fields must exist in the XML.

To improve your suggestion, the E4X solution for a field that may not exist would be to use the attribute() function. It will not throw an error if the attribute is missing. It just returns null.

attribute("source") == "Amazon" && attribute("price") == "599.99"
Mathias Wedeken

Josh,

cool summary. E4X is one great beast, that´s true.something that is also worth noting is that you don´t have to format the xml when you generate it within your class, it will be done automatically, so there´s no need anymore for “ignore whitespace2 or what it was called again. that´s also very nice when outputting the xml to a server side PHP script to save it to disk, the xml file you´ll get is very well structured and perfectly readable, not like the old times where you had just a huge pile of code…

anyways, just something.

best

mathias

Logesh Kumar P

Really useful information and easy to understand….

Thanks,
Logeshkumar P.

Yasir

It is very helping example. I have some problem to filter data in XMLList. I want to show the elements of the XMLList in tree control. But I don’t want to show the leave nodes.
May any one help in that problem?

Thanks

Josh Tynjala

Yasir, if you don’t need the leaf nodes at all, you can delete them. Otherwise, you should learn to create a custom implementation of mx.controls.treeClasses.ITreeDataDescriptor.

cdm

I found a problem in using the same syntax:
var items:XMLList = data.item.(source == “Amazon” && price <400);

into a binding expression like

dataProvider=”{data.item.(source == “Amazon” && price <400)}

it will not accept the &&

Any idea – apart from doing it in AS?

Josh Tynjala

cdm, I’m guessing the problem is that you need to use &amp; instead of the straight ampersand. MXML is a dialect of XML, so it requires encoding for special entities (exception: CDATA blocks, but that’s not possible in an attribute).

Lefty

Hi,
thanks for a great and utterly useful post!
I do have a problem though; any idea if it’s possible to add elements together and compare them to a variable within the parantheses?

Like this for example:
XMLList.(available == “Yes” && (from_price + from_tax + from_postage) >= minCost && (to_price + to_tax + to_postage) <= maxCost).@ID;

Josh Tynjala

Yes, Lefty. That should be possible.

VegasPat

Hi Josh,

In your example, how would you filter an item that contains the number 3 in it’s name so that your results would return Playstation 3 and X-box 360? Thanks for your posts and your help.

Josh Tynjala

VegasPat, this should do it:

var with3:XMLList = data.item.(@name.indexOf("3") >= 0);
Nick

You deal with clean XML feed here, but i like em dirty.

I keep getting the error -
Error #1009: Cannot access a property or method of a null object reference.
-because not all my nodes have the attribute ‘contactname’ so i need to check if that attribute is apparent before trying to add it to my array.

Josh Tynjala

Nick, I’m guessing you included some XML in your comment, but the blog software stripped it out. Use &gt; and &lt; in the future to replace > and <.

If an XML element may not have an attribute like you describe, then @contactname won’t work because you’ll get a runtime error. Instead, use attribute("contactname"). This XML method is available for the exact use-case you described.

evolutioneer.com/blog » Blog Archive » Manipulate XML Data in AS3

[...] Ways to Use EX4 To filter XML Data in ActionScript 3 (Thanks to: Josh Tynjala at Josh Talks Flash) [...]

mr.moses

how would you filter if there was more than one source element in each item element?

instead of having two Wii items, if there is one Wii item with multiple source elements (one for Amazon and one for Target), the Wii item will is not selected if you filter on Target.

<item name=”Wii”>
<source>Amazon<source>
<source>Target<source>
</item>

Josh Tynjala

mr.moses, with two source elements, the following seems to work for me:

data.item.(source.contains("Target"))

I didn’t know about the contains() method until today, so thanks for the great question.

Matt Long

Is there a way to filter and display by a specific date…
For example if I have a list of events but I only wanted to display the 4 most recent, is there a way to grab a date element from the XML file and compare it with the Date/Time on a users machine?

Josh Tynjala

Matt Long, it should be possible. How easy it will be depends on how the date is formatted in the XML, though. The Date constructor with one argument or Date.parse() could be useful for extracting date values for comparison.

Daniel

Is there any way to only get the parent nodes with toXMLString() and not the child nodes?

I have the following:

<root>
<parent></parent>
<parent><child></child><child></child></parent>
<parent></parent>
</root>

All I want is to filter out the above XML and only get the parent nodes printed out without their children.

Josh Tynjala

Daniel, you might be able to loop through the parent elements and use setChildren("") to remove the children.

shen

I have following XMLLIst I want to get books and Songs as tabnavigator tabs(dataprovider),Is there way to get XMLLIst to dataprovider or array collection.because under the dynamic properties always change books,songs sometimes it may books,films,songs.
so I want to appear tabnavigator tabs books,films,songs..

1

2

Thank you in advance,Hope flex star may help me!!

shen

1

2

andy.edmonds.be › links for 2009-07-12

[...] Ways to use E4X to filter data in ActionScript 3 – Josh Talks Flash (tags: xml e4x filtering flex actionscript) [...]

Dirk

Thanks a lot. Think this will solve problems!

Josh Noble

Thanks muchly for this.

Parenthesis – the round mystery « Max Blog

[...] There are a few nice posts about E4X filters on this blog: http://joshblog.net/2007/05/08/methods-to-filter-data-with-e4x-in-flash-9/ [...]

Giles

Josh, absolutely great post and has helped me out a lot but I seem to have discovered an intractable problem. I am attempting to filter for two attributes within descendants.

I’ve tried using every method I could think of – (contains); attribute(“”); hasOwnProperty(“@”); att==; but none will work. Flash seems to ignore anything that comes after the &&.

Here is one of the lines I’ve tried, can you think why it won’t work?

mediaFilterDays = filterDays.(descendants(“programme”).(@genre == Filter && @media == mediaFilter));

jon

trying to figure out how to sort if an item has more than one of the same node type…

such as:

<item name="Consoles">
<source> PS3 </source>
<source> PS2 </source>
</item>

if I searched for all items with PS2 this would be skipped with the methods used above…

Josh Tynjala

Jon, if you have multiple elements as children, it might not be possible to do it in a single statement. You might have to put source into an XMLList and search that separately.

However, you might also look at contains() on XMLList. It might be useful here. I’ve never played with that one, though.

Kristin

Hey, great article, but I’ve run into a snag with your last example. I cut and pasted your code and get this in my output window:

(your code and your xml file which I swear I did not change)

var filtered:XMLList = data.item;
var fields:Array = [{name: "source", value: "Amazon"}, {name: "price", value: "599.99"}];
       
var fieldCount:int = fields.length;
for(var i:int = 0; i <fieldCount; i++)
{
    var fieldName:String = fields[i].name;
    var fieldValue:String = fields[i].value;
    filtered = filtered.(child(fieldName) == fieldValue);
	trace(filtered);
}

this code is supposed to return Amazon items worth 599.99, however it returns 3 Amazon items. It's returning 2-599.99 items and 1-399.99 item which is not what's in the xml file.

output results

Amazon
399.99

Amazon
599.99

Amazon
599.99

I’m new to xml and AS3 so I wanted to make sure this wasn’t happening because I’m using Flash CS4 (since your original post is almost 3 years old)

Josh Tynjala

Put the trace() call after the loop and you might see the correct result. During the loop, it’s still filtering by field name and value, and that means it will search over each of the items more than once.

Kristin

Thanks Josh, that was it and I should have tried it. I only had intro classes to Flash MX2004 and that’s been almost 3 years ago. Kinda rusty.

Kristin

By the way, while I’m here… I’m trying to write a picture viewer program and I’ve seen a lot of tuts on the web, but they don’t seem to address my needs. I have 3 xml elements: name, file (an image), and comments. I have so many image files that I’ve chosen to load them into a list component which I can load up with name data. For some reason I’ve hit a road block in that I don’t know how to link the selectedItem in the list box so I can display the other elements on the page. I hope this isn’t too complicated a question to ask. Thanks

Auzzie

Great post, I was able to use a couple of scripts above but ran into an issue and was wondering if anyone had some insight:

I have some data that I separated into labels of a combobox:


Features3
Features1,Features2,Features3
Features2,Features3
Features1
—-
if (featuresCombo.selectedLabel != item.FEATURES)
return false;

return true;
—-

If you select ‘Features3′ for example it doesn’t return the selections that have multiple entries separated by commas.

Is there a way to filter based on the occurance of a string rather than matching the whole string?

I tried to use the (contains) function from above and was getting this error:Error #1123: Filter operator not supported on type mx.utils.ObjectProxy.

Thanks for any help.
-Auzzie

Leave a Comment

Note: New comments may need to be approved before they appear.

Some HTML allowed in comments: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

To display code in comments: <pre>Code here. May be multiline. Format XML with &gt; and &lt; entities.</pre>