Daily Archives: January 5, 2011

PHP Ecterators

“…When in the why and the wherefore is neither rhyme nor reason?”

Shakespeare, The Comedy of Errors II.2

When it comes to the common workhorse task of iterating over a collection, there are nearly as many approaches as there are languages. And these varied implementations often demonstrate the relative strengths or weaknesses of each platform.  In languages with dynamic typing and closures, iterating over collections is typically succinct and elegant. Lacking these benefits, you get code-bloat train wrecks like C++ iterators and templates.

Fortunately, good approaches often get reused. Both Ruby and Smalltalk have what I alone call the ecterators: those venerable iterator methods whose names, inspired by Alice’s Restaurant, end in “ect”: select, reject, collect, inject, and detect.  In a single line of code, you can apply a code block across a collection and get the result.  I often think in terms of the ecterators, so I miss them when working in a language that doesn’t have them.

Such was the case recently when working in PHP.  Modern versions of PHP have helpful functions like array_filter, array_reduce, and array_map, and with PHP 5.3’s lambdas/blocks and closures, you can create a Ruby-like feel.  But those non-rhyming names and inconsistent parameters just don’t do it for me.  You might say PHP lacks some reason and a lot of rhyme.  So I took a moment to write some simple alias functions, like so:

        function select($array, $callback) {
            return array_filter($array, $callback);
        };
        function collect($array, $callback) {
            return array_map($callback, $array);
        };
        function inject($array, $initial, $callback) {
            return array_reduce($array, $callback, $initial);
        };

So now I can write familiar code:

        $numbers = array(1, 2, 3, 4, 5);
        $even_numbers = select($numbers, function ($ea) { return $ea % 2 == 0; });
        $squares = collect($numbers, function ($ea) { return $ea * $ea; });
        $sum = inject($numbers, 0, function ($sum, $ea) { return $sum + $ea; });

There, reason and rhyme.