Using Hamcrest to Filter ArrayCollections

August 11, 2009 at 12:03 PM | ActionScript | View Comments

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 you want to filter it by, say, price, size and (of course) color.

Because you're a good programmer you always try to do the simplest thing that could possibly work first, and end up with a function something like this:

function laptopFilter(laptop:Laptop):Boolean {
    if (laptop.price > appliedFilters.maxPrice)
        return false;

    if (laptop.size > appliedFilters.maxSize)
        return false;

    if (laptop.color != appliedFilters.preferredColor)
        return false;

    return true;

Cool - that defiantly works, and it's pretty simple.

"But wait a second", you're thinking, "what happens when the rules get more complex? That function is quickly going to loose its simplicity!"

Enter, Hamcrest.

Hamcrest is a library of "matchers" which are often used to build unit tests. Here is a quick example of a unit test written with Hamcrest:

function testApplePicker():void {
    var apples:Array = ApplePicker.pick("granny smith");
    assertThat(apples.length, greaterThan(4));
    assertThat(apples, everyItem(hasPropery("color", "green")));

But, of course, there is no reason that these same matchers shouldn't be used for other things too.

Other things like, say, filtering an ArrayCollection:

var filters:Array = [
    hasProperty("price", greaterThan(1000)),
    hasProperty("color", either("red").or("blue").or("green")),
    hasProperty("size", between(10, 15))

var laptopFilter(laptop:Laptop):Boolean {
    return allOf.apply(null, filters).match(laptop);

Of course, I've hard-coded the values in this example, but it doesn't take much creativity to imagine how they could be dynamically generated, keeping the code nice and simple while still allowing for complex rules.

So, that's how I filter ArrayCollections :-)