Filtering an ArrayCollection On Multiple Properties/Values
In my application I have an ArrayCollection containing some objects. I only want to display certain subsets of these objects in certain view components, and I can achieve that by applying a filter function to the collection, but sometimes you may wish to filter on multiple properties or property values. First, let’s take a quick look at how the filter function works:
Basic Filtering
Each time the filter function gets called it receives an Object (an item from your ArrayCollection) and must return true or false – false if you want the object to be filtered out of the collection.
Let’s suppose we have a collection of objects with a name property, and each object looks a bit like this: {name:”someNameString”}. We may want to only show objects with the name “AAA”. Therefore our filter function may look like this:
[as]
collection.filterFunction = myFilterFunction; //set the function to use
collection.refresh();//must do this to cause anything using the collection to update.
private function myFilterFunction (item:Object):Boolean
{
return ( item['name'] == “AAA” );//returns true only if our ArrayCollection item ‘name’ property is “AAA”
}
[/as]
Filtering On More Than One Value
Now what if you want to filter on multiple properties or multiple property values? Say we wish to filter properties that have either the name value “AAA” or the name value “EEE”? We could create a filter function like this:
[as]
private function myFilterFunction1 (item:Object):Boolean
{
return ( item['name'] == “AAA” || item['name'] == “EEE”);
}
[/as]
… but what if we now wish to filter objects with the names “AAA” or “BBB”? Well we could create yet another function to do that, and what if we want to filter on values “BBB” and “CCC” ….. as you can probably tell, if we have multiple values and wish to filter on any combination of these values it’s going to lead to a combinatorial explosion of filter functions very quickly.
It would be better if we could send these values in as arguments to our filter function and have our function check against all of them, but sending in arguments doesn’t seem to be an option, so instead we can set a class level property on our main class which can hold all the values we wish to filter on.
Creating A Basic Filtering Class
Rather than do that, it’s best to encapsulate this functionality in to a separate class with the specific responsibility of taking care of our filtering. We can create a class with a property that can hold an array of filter values, and a function on that class which will generate our filter function for us too.
If that was as clear as mud, let’s say our new class is called FilterSelector and see how we would use it in our application:
[as]
//IN OUR MAIN CLASS…
var fs:FilterSelector = new FilterSelector();
fs.filterField = “name”; //filterField is a property we’ve created to hold the field we want to filter on
fs.addFilterTypes(“AAA”, “CCC”, “EEE”);//the values we want to filter on.
collection.filterFunction = fs.generateFilterFunction();//USE THE OBJECT TO GENERATE THE FILTER FUNCTION
collection.refresh();
[/as]
Inside the FilterSelector class, our generateFilterFunction() may look like this:
[as]
public function generateFilterFunction():Function
{
var f:Function = function(item:Object):Boolean
{
for each(var type:String in _filterTypes)
{
if(item[filterField] == type) return true;
}
return false;
};
return f;
}
[/as]
As you can see, the generateFilterFunction() function returns an object of type Function. What it actually does is to create an anonymous function (a function without a name), which will loop over all our filter types, and returns this anonymous function to us, which we then set as the filter function. This anonymous function will return true as soon as any match is found, else return false if there are no matches.
Extending The Class
There are lots of places you can go with this. It may also be the case that you want to filter on different properties (ie rather than “AAA”, “BBB”, you may wish to filter on “name”, “label”) in which case you could accept multiple values for the filterField property too, then set up a nested loop for both property & value, and cross check each one.
In reality you may actually need multiple filter functions, as they may be markedly different. You could create another class with a specific type (eg IFilterFunction) and set the function you wish to use as a property on the FilterSelector class – this could get tricky, depending on how complex and specific your filters are, or you could do something similar to what is described in these posts: here & here.
Apologies for this rushed post (and hence lack of embedded working Flex example). Here is a link to a working example, exported as a Flex Project Archive .
I’ve not looked at the filtering in Flex ArrayCollections before. If you think you have a better solution feel free to chime in :]
1 Comment so far
Leave a reply

Using Hamcrest to Filter ArrayCollections …
Neil Webb’s post on filtering an ArrayCollection on multiple property/values reminded me of how I recently solved exactly the same problem using Hamcrest-AS3.
So, you know the story: you’ve got an ArrayCollection that’s full of, say, Laptops, and y…