Gentle Introduction to Functional PHP for WordPress Developers: Using Filter and Map on Arrays of Posts

Pen function |functional programming in WordPress

Fred’s post last week was about filtering through arrays of posts and ordering them. He foreached through arrays, and then used PHP’s usort() function to get a list of alphabetized posts with comments open. That got me thinking a little bit about a topic I very briefly touched on in my summary of the JavaScript ecosystem: functional programming.

Functional programming is one of the most interesting and valuable things I’ve started to understand in the last few years.

I think functional programming is one of the most interesting and valuable things I’ve started to understand in the last few years. It’s not for everyone though, and it’s not always that relevant to working with WordPress. But it actually shines when doing things like processing and sorting arrays in PHP (again, as Fred’s article offered a way of doing) in a more compact and fluent way. So my hope is to explain some more of the theory of this weird “buzzword” while also giving you concrete and practical uses that you may put it to.

A Quick Summary of Programming Paradigms

foreach is Procedural

Filtering an array with foreach is very “procedural.” That is: we’re describing to the PHP interpreter exactly each step it should go through and what each intermediate state should be.

PHP’s foreach loops are one of the most useful things a programmer learns early on. The language around them—”For each of the items in this list, do the action inside”—is easy to start to get your head around, somewhat easier than even the simpler functional programming concepts we’ll cover today.

To start to get concrete, we’ll copy some code from Fred’s article. He was looking to create a list of posts with comment open and closed, and did it this way:

$posts_by_comment_status = array();
foreach( $posts as $post ) {
    $posts_by_comment_status[ $post->comment_status ][] = $post;
}

The result was an array with two indexes — open and closed with an array of posts under each. This is quite effective as a way to create that outcome.

It’s also a very “procedural” way to do it. That is: we’re describing to the PHP interpreter exactly each step it should go through and what each intermediate state should be. If parallelization (doing this task in parallel with many different processes) were possible in PHP, it wouldn’t happen here because we’ve defined the procedure as being a linear iteration through one array while inserting elements sequentially into another.

This method is also relying on data from outside the foreach loop, principally the $posts_by_comment_status array which is created before it and then used inside of the loop. In this way, the foreach loop’s “action” has knowledge of what’s outside of it. This is common in WordPress programming, but makes the action inside the foreach “stateful” in that it knows about and impacts the world in which it lives.

array_filter is Functional

To accomplish the same basic goal — generate a list of posts whose comment_status is open and another list whose comments are closed — we could instead use a call to PHP’s array_filter function. I’m going to quickly show the code — ripping the Band-Aid off — and then we’ll slow down and explain it and all the component concepts.

$posts_with_closed_comments = array_filter(
    $posts, 
    function($post) {
        return $post->comment_status === 'closed';  
    }
);
$posts_with_open_comments = array_filter(
    $posts, 
    function($post) {
        return $post->comment_status === 'open';    
    }
);

Now this code only works in PHP 5.3 and above, but I’ve written it in this way because most WordPress code today is fine with that assumption, and the 5.2-compatible way is slightly harder to make sense of your first time. (For those who need it, we’d pass as the second argument the names of functions that are defined separately where the function(){} is.)

What are we doing in this example? We’re using the ability to filter an array to make a new array with only the elements we want. How does this filtering happen? Let’s look more closely at array_filter().

How array_filter() Works

In PHP, array_filter needs two arguments (and can accept three). They are:

  1. The array of elements you want to filter.
  2. The function you want to use to do the filtering. This function should return true for values the filters should let through, and false for those it’ll remove.
  3. (Optional) A flag to pass only keys, or both keys and values, of the array to the function. The default, which we’re using here, is passing only the values.

So here we’re able to get an array of just the posts with closed comments by passing as the second argument a function that returns back the result of a comparison between the string value 'closed' and the string value (either 'open' or 'closed') that will be the value of the post’s comment status. Those posts whose comment status is 'closed' will thus see a true returned from the filtering function and be kept. Those that don’t will return false and therefore be discarded. We can then do the opposite to get those with comments open.

Assuming you truly wanted both sets, not only those in one or the other state, the need to do the array_filter twice is a bit of a win for the procedural method. But usually for me a foreach is only keeping some small subset, and this is exactly what array_filter is for.

What’s Functional About This Example?

The core thing that is “functional” about our examples is that we’re passing a function into another function: array_filter().

One of the more common errors people make after encountering “functional” programming as a concept is to this it’s just about having functions. Personally, I spent more than a year of my life believing I was doing cool, cutting-edge “functional programming” because of this misunderstanding. The core thing that is “functional” about our examples is not just that we’re using the keyword function (all WordPress programming should do that!), but rather that we’re passing a function into another function: we pass our nameless function (which could maybe have a name like returns_true_if_comment_status_is_open()) into array_filter() as its second argument.

The other thing to notice is that the function we’re passing in is “stateless”: it has no knowledge of the rest of code, and does not need it. It merely knows it receives a $post object as input, and needs only that object to determine what to do. In this way the function is said to be “pure” or “mathematical.” (Conversely, “procedural” functions are almost never “stateless”: their procedures only make sense when the world is in a given configuration which isn’t explicitly elaborated and must be built up by the programmer. A modest example is the empty array Fred created at the start of his foreach.)

filter() is one of the best gateways into functional programming, because it requires you start to understand that functions can be very small, very short, and very simple. It points to the expressive benefits you can get from this sort of simple compactness when you pass functions into each other. Procedural code, so common in WordPress, is typified by 10-100 line functions. Getting out of that habit with something like filter is a great way to start.

Transforming Items in Arrays with a Map

The filter and map operations are the canonical gateways into functional programming, so I have to bring up map.

The filter and map operations are the canonical gateways into functional programming, so I have to bring up map. To do so, we can continue to riff on Fred’s idea of getting posts, filtering them out, and then alphabetizing them. But I’m going to cheat a bit to keep it simple: Fred’s result screenshot only shows names — not links, and not other post data which a real-world use probably requires. But for example purposes, this simple need makes it much easier to use array_map() to accomplish our goal. So we’ll cheat.

We’re going to get an alphabetized list of post titles, without using the very useful usort() function Fred used. (You may not have noticed, but usort() itself is “functional”: you pass a stateless function as an argument to another function.) We’ll instead use array_map() and PHP’s very simple sort() function to alphabetize by post title.

Using array_map

Here’s a rough edge of PHP: the order of parameters to array_filter and array_map are opposite. Where array_filter expects your array and then your function, array_map wants your function and then your array.

With that in mind, we can create an array of post titles from an array of post objects with a simple snippet like this:

$list_of_titles = array_map(
    function($post) {
        return $post->post_title;
    }, 
    $posts
);

Again, we’re using a little function to do an operation inside another function. I think of map as a little worker: you send the function you pass to map out to your list, and it does the transformation on every element of that list. To get an array where every element was double the old value, your mapping function would be a simple return $number*2;. So for us, the function takes each $post element from the array and gets its post_title property. What results is an array with identical order, but with only the post titles in the places where the entire post objects were in the original array.

sort() in Short

PHP can alphabetize for you; sort() does it. sort() takes an array, and changes that array to be sorted alphabetically.

The one thing worth some attention is another oddness of PHP: sort changes (“in place”) what you hand it, and returns either true or false based on whether or not it’s able to do the sort. This example highlights the issue I forgetfully have regularly:

$fruits = array("lemon", "orange", "banana", "apple");
$sorted_fruits = sort($fruits);

What this produces:

fruits[0] = apple
fruits[1] = banana
fruits[2] = lemon
fruits[3] = orange

sorted_fruits = true

Just noting the gotcha because if you try to go on to use $sorted_fruits you will be quite frustrated that it’s true and not your sorted array. Otherwise, sort‘s simple enough: it sorts strings in an array alphabetically.

Combining Mapping and Sorting

Now that we understand both array_map() and sort(), we just need to combine the two to get an alphabetized array of post titles:

$list_of_titles = array_map(
    function($post) {
        return $post->post_title;
    }, 
    $posts
);
sort($list_of_titles);

Now $list_of_titles will contain all the titles from the array of $posts, in alphabetical order.

Just to make a point again, the clever among you may have thought (as I did when assembling the example code): Why don’t I just wrap the sort() around array_map() and save myself a line? The reason is again, as we touched on in our section on sort(), that then your $list_of_titles would just take the value true— which would be completely useless to you.

Now You Can Filter and Map

Functional programming is a very useful tool for some jobs, and filtering through a list of items is one of the clearest ones.

Functional programming is a fun thing to play with, and a very powerful tool for many types of problems. Contrary to the impression a reader might reasonably get from my writing about it kind-of-a-lot, I don’t think it’s the new hotness that every WordPress developer needs to know; far from it. But I do think it’s very useful tool for some jobs, and filtering through a list of items is one of the clearest ones.

filter and map are very useful to know and understand, even if you never go a step deeper into functional programming. I hope, though, that you found the different tactics used here to solve the problems Fred solved last week interesting and informative. Happy hacking!

Image credit: Vestman


9 Responses

Comments

Pingbacks