For(each) alternatives: introducing “array_map” and “array_filter”

19th December 2022, 11:05

Being a programmer, you’re probably very (very) familiar with the for and foreach loops in PHP and Javascript. So familiar in fact, that you’re kind of resenting them whenever you need to create a subset from a set.

We’re here to tell you that there’s more to the world than just foreach-ing. Introducing: map and filter

Let’s imagine we have a set of data as such

$books = [
    [
        "id" => 1,
        "title" => "1984",
        "author" => "George Orwell"
    ],
    [
        "id" => 2,
        "title" => "To Kill a Mockingbird",
        "author" => "Harper Lee"
    ],
    [
        "id" => 3,
        "title" => "The Great Gatsby",
        "author" => "F. Scott Fitzgerald"
    ],
    [
        "id" => 4,
        "title" => "One Hundred Years of Solitude",
        "author" => "Gabriel García Márquez"
    ]
];

Scenario 1: Filtering a PHP array

Let’s say we only want to get the uneven id‘s from this list. Normally you would do something like this:

// create a new array to contain our filtered books
$filteredBooks = [];

// iterate over each book
foreach ($books as $book) {
    // check if the id is not divisible by 2
    if ($book['id'] % 2) {
        // add the book to the filtered books
        $filteredBooks[] = $book;        
    }
}

// output the filtered books
foreach ($filteredBooks as $filteredBook) {
    echo $filteredBook['id'] . "\n";
}

The above example is basically how “anyone” would create a subset in PHP.

However, there is a much faster (and cleaner) way of writing the above. Using a method called array_filter we can directly create a subset without having to declare a separate variable before filtering our set.

// declare filteredBooks based on the results of array_filter
$filteredBooks = array_filter($books, function ($book) {
    // return a boolean indicating if the id is not divisible by 2
    return $book['id'] % 2;
});

foreach ($filteredBooks as $filteredBook) {
    echo $filteredBook['id'] . "\n";
}

Much cleaner right? The array_filter method relies on the “boolean result, returned by it’s closure”, meaning it filters any “false” result.

Scenario 2: Mapping a PHP array

In this scenario we want to do something else completly. We just want the title from each book in our books set. That’s easy right? You’ll probably write something like the following:

// declare a new array to contain our titles
$titles = [];

// iterate over each book
foreach ($books as $book) {
    // get the title and add it to our titles array
    $titles[] = $book["title"];
}

// output the titles
foreach ($titles as $title) {
    echo $title . "\n";
}

If you’re anything like us, you’ll probably get this gnawing feeling that this method seems… too much. Even overkill perhaps. You’re right.

Just like array_filter is a good method to create subsets, the array_map function excels at basically “remapping” an array. To use array_map we could do the following:

// declare titles based on the result of array_map 
$titles = array_map(function ($book) {
    // return the book's title
    return $book['title'];
}, $books);

foreach ($titles as $title) {
    echo $title . "\n";
}

In this scenario, it might seem excessive to use an array_map because we could just iterate over the original set right? Well, that’s why we’re going to combine both:

Scenario 3: Mapping a filtered PHP array

Using what we learnt above, we could combine our knowledge to do some very cool things.

Let’s say you would like to get all author‘s from the books set where the title contains the letter a. We could achieve this by first using array_filter to create a subset and then map the author‘s.

Normally in PHP that would look something like this:

// create a new array to contain our filtered books
$filteredBooks = [];

// iterate over each book
foreach ($books as $book) {
    // check if the title contains the letter 'a'
    if (strpos($book['title'], 'a') > -1) {
        // add the book to the filtered books
        $filteredBooks[] = $book;        
    }
}

$authors = [];

foreach ($filteredBooks as $filteredBook) {
    $authors[] = $filteredBook['author'];
}

Or if you’re slightly more experience you would probably do this using only one iteration:

// create a new array to contain our filtered books
$authors = [];

// iterate over each book
foreach ($books as $book) {
    // check if the title contains the letter 'a'
    if (strpos($book['title'], 'a') > -1) {
        // add the author to the authors
        $authors[] = $book['author'];
    }
}

// output the authors
foreach ($authors as $author) {
    echo $author . "\n";
}

This still feels kinda “yucky” right? We agree. So here’s how we would do it:

// this blocks reads from bottom to top because results as chained
// declare authors containing the result from array_map
$authors = array_map(function ($book) {
    // return the book's author
    return $book['author'];

// use the subset from array_filter as the argument for array_map
}, array_filter($books, function ($book) {

    // return a boolean indicated the book's title contains the letter 'a'
    return strpos($book['title'], 'a') > -1;
}));

// output the authors
foreach ($authors as $author) {
    echo $author . "\n";
}

Because array_map accepts an array as an argument, and array_filter returns an array, we can “chain” these functions using their respective outputs.

Conclusion

The functions array_map and array_filter are very useful, especially when combined. The drawback to chaining these functions like in Scenario 3, is that they might become “unreadable” for inexperienced programmers.

However, mastering these (and other functions like array_reduce, array_intersect, etc) will make life a lot easier for you in the long run. These functions also use internal libraries to speed up their execution, so if you’re iterating over massive collections, this is the way to go.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *


Recent posts

Javascript: Synchronous imports

Recently we’ve come across a challenge. Well not really a challenge for normal developers, but one for developers that like […]

Read more
PHP: array_map vs array_map with callback vs foreach

So we’ve come across plenty of posts comparing / measuring performance of the PHP array_map function vs foreach. So we […]

Read more