Discovering Higher Order Functions [refactoring to Collections]

Coding (Php 7.x)

Aug 28, 2020

There are amazing ways to improve your PHP, map, reduce and filter are some of them
 
/img/blog/discovering-higher-order-functions-php.jpg

Introduction

 

One of the goals I set at the beginning of every year is to read a certain amount of books.

 

I am often behind with this particular goal so I take advantage of times such as a holiday to try to recuperate some times.

 

One of the books I put my hands on right now is “Refactoring to Collections” by Adam Wathan and boy if that’s an amazing read.

 

In the book, Adam illustrates some little flaws in PHP and how with, with a few expedient you can improve the readability, thus the quality of your application.

 

The content of this post are easy-to-understand nuggets that I have learned from Adam, the goal is mapping, reducing and filtering by taking advantage of higher-order functions.

 

 

Become a Patron!

 

 

Procedural vs Declarative Programming

 

Let’s start by describing the two bigger elephants in the room,

 

First one:

 

Procedural programming:

 

I am quite sure you have already heard of it, you may also call it Imperative Programming or structured programming 

 

(if not please read the basics of PHP before keep reading).

 

This type of programming is one of the most popular and the one that is generally taught in computer science introduction classes.

 

The paradigm consists of dividing the code into blocks and wrap a set of instruction inside the latter.

 

Each of these blocks called functions handles one task and the program invoke this function whenever this task is required.

 

The problem with this is that often, these functions do not do what they are supposed to do, more often than not they do more that we want.

 

here is more about Procedural programming

 

Now, look at the following snippet.

 

function getUserEmails(array $users) :array
{
    $emails = [];
    for ($i = 0; $i < count($users); $i++) {
        $user = $users[$i];
        if ($user->email !== null) {
            $emails[] = $user->email;
        }
    }
    return $emails;
}

 

What we want it pretty straightforward and is actually understandable by a very good naming too,

 

I am going to call this function every time I need an array containing the list of emails from the users registered in my application.

 

This is what I am saying what my code is doing instead is, creating an empty array, creating a counter that starts at 0, looping through the users while checking if the counter did not increase too much, create a user variable, checking if the user has set an email, and eventually after all this returning the list. 

 

This is what you usually see on procedural code.

 

Let’s try this again, but this time using some “declarative” 

 

Repeating the exercise in SQL this is what we have to write:

 

SELECT email FROM users WHERE email IS NOT NULL

 

Surely on the background, there will be some sort of loop and checks but the point I am trying to send across is that it does not matter, it does not matter to us at least.

 

Higher-Order Functions

 

Map

 

A higher-order function is a function that takes another function as a parameter,

 

returns a function, or does both.

 

Higher-order functions are powerful because they let us create abstractions around common programming patterns that couldn't otherwise be reused.

 

Let’s look at an example:

 

$dishesPrices = [];
foreach ($dishes as $dish) {
    $dishPrice = '$' . number_format($dish->price / 100, 2);
    $dishesPrices[] = $dishPrice;
}
return $dishesPrices;

Like in the example within the section above what we are doing consist in creating an empty array and adding the element to it by the application of some operation to every item in the existing list.

 

The two snippets, in fact, look very similar.

 

If we abstract the specific cases we can pretend to have something like the code below.


 

$results = [];

foreach ($items as $item) {
    // do something and get $result 
    $result = $item->requiredData;

    // then save each $result into the array
    $results[] = $result;
}

return $results;

 

Now we realized that what the two functions do is basically the same, but we have a different procedure on how to get there.

 

What we can do to be able to use the abstract code is to extract these bits of code into their own block of code for this purpose we are going to use a PHP feature called anonymous functions.

 

Explaining what Anonymous function do will take an article by its own so I have decided to link to this fantastic blog post by Jacob Emerick.

 

The anonymous function will only take care of the code that is specific for the case, relative to the current item in the loop, it elaborates what he needs to do with it an eventually return it to the main function.

 

Here are one example:


 

$doStuff = function ($dish) {
    return $dishPrice = '$' . number_format($dish->price / 100, 2);
};


$results = [];

foreach ($items as $item) {
    // we are now passing the item as attribute and get the result
    $result = $doStuff($item);

    // then save the returned $result into the array
    $results[] = $result;
}

return $results;

 

Et violet! 

 

Now you can see as apart from the anonymous function all the code has been abstracted and can be used for multiple cases

 

The process that you have just seen has a name and it is called Mapping.

 

We use map function when we must transform items in an array into something else. 

 

What we need to make a map function work is an array of items (or any Traversable) and the instruction of what the map needs to do enclosed into a function.


 

function map($items, $func)
{
    $results = [];
    foreach ($items as $item) {
        $results[] = $func($item);
    }
    return $results;
}

// anonymous function on the emails’ customers example
$getCustomersEmail = map($customers, function ($customer) {
    return $customer->email;
});

// anonymous function on the names’ dishes example
$getNamesDish = map($dishes, function ($dish) {
    return '$' . number_format($dish->price / 100, 2);
});

 

Generally, you should use a map when you are 

  • Extracting one or more fields from an array of objects
  • Populating an array with refined objects from raw data
  • Change the format of the items in an array;

 

 

Filter

 

Now that you understood how mapping works we can explore more option of what we can do with higher-order functions.

 

For simplicity sake, we are going to keep the same example but we are varying the task we need to perform.

 

In the previous paragraph, we mapped through a list of dishes and format an output that showed the prices in dollars. 

 

What if we only want to show dishes that cost $10 or less?

 

As you have seen from the list above mapping, even though the best choice when it comes to transforming data, is not the best choice when we have to filter it.

 

Let’s get into it!


 

$cheapDishes = [];

foreach ($dishes as $dish) {
    if ($dish->price <= 10) {
        $cheapDishes[] = $dish;
    }
}

return $cheapDishes;

 

Looks familiar?

 

Let’s enclose the algorithm into its own function


 

$doStuff = function ($dish) {
    // the code below returns a boolean that will be checked inside the filter function
    return $dish>price <= 10;
};


$cheapDishes = [];

foreach ($dishes as $dish) {
    if ($doStuff($dish)) {
        $cheapDishes[] = $dish;
    }
}

return $cheapDishes;

 

This eventually can be transformed in what is called the filter function

 

function filter($items, $function) {
   $result = [];

    foreach ($items as $item) {
        if ($function($item)) {
            $result[] = $item;
        }
   }
   
  return $result;
}

$getCheapDishes = filter($dishes, function ($dish) {
    return $dish>price <= 10;
});

 

 

In this case, we do not transform any data from the given array,

 

What we do is to filter out any items of the array that do not match our needs.

 

The way it works is by telling which item we want to keep thanks to the callback function that, in case the condition is met return true if we want to keep the element and false if we what to remove it,

 

You might have noticed that a difference with the map function is that in here we do not transform any items, but rather remove the items we do not want. 

 

The items that are inside the new array are the same exact items that were in the old one.

 

Reject

 

Surely it always depends on what you need to do but in some cases, it might be better cost-effective to calculate the items that you want to discard instead of the items that you instead of the one you want to keep.

 

The reject method does exactly this.

 

Using it is practically the same on the filter function but in this case, you only change the value of the condition.


 

function reject($items, $function){
    $result = [];

    foreach ($items as $item) {
        if (! $function($item)) {
            $result[] = $item;
        }
    }

    return $result;
}

 

The difference between this and the code inside the filter() method is here the condition consists into a negation.

 

Another way to do that is to use the filter method and negation the callback response.


 

$getExpensiveDishes = filter($dishes, function ($dish) {
    return ! $dish>price <= 10;
});

 

Reduce

 

Sometimes what we want to do is to get a single value out of an array of elements,

 

This can be an arithmetic operation, of something to do with strings.

 

Anything really,

 

As long as the function returns a single element;

 

Let’s return to our dishes example but this time we will calculate the complete amount of price for all the dishes together.

 

As per normal this is what we will usually do:


 

$dishesPrice = 0;

foreach ($dishes as $dish) {
    $dishesPrice = $dishesPrice + $dish->price;
}

return $dishesPrice;

 

Let’s analyze the code here,

 

The first thing we need is to create a variable that will hold the initial value.

 

Then the process would be to iterate over the dishes variable and sum the price of the dish to the current value of the $dishesPrice variable.

 

As we did in the example above let’s make this example a bit more abstract:


 

$result = 0;

foreach ($items as $item) {
    $result = $result + $item->price;
}

return $result;

 

Now that the code is more abstract a thing that that strikes the eye is that 0 in the first line.

 

What if we are working with strings?

 

It would probably be an empty string instead.

 

What about if we had an initial value of 100 rather than 0? 

 

To solve this problem and make the snippet abstract and usable on various occasion we should set the initial value into a variable that we can modify:


 

$initial = 0;
$result = $initial;

 

This makes way more sense because now we can instantiate the $initial value somewhere else and save it to a broad $result variable afterwards,

 

You will see soon what I mean.

 

Also, if we change the type of data we are working with we can only change another $inicial.

 

Now the part that matters and the one that needs to be extracted is the line inside the foreach itself.

 

In that line, we have 2 different variables, the variable that is taking count of the sum and the actual value.

 

Now that we know what we are using we can create a callback function that will consider all the parts in this scenario.


 

$items = $dishes;

$doStuff = function ($dishesPrice, $dish) {
    return $dishesPrice + $dish->price;
};


$initial = 0;
$result = $initial;

foreach ($items as $item) {
    $result = $doStuff($result, $item);
}

return $result;


Now it’s the time to distill this snippet into an higher-order function:


 

function reduce($items, $callback, $initialValue) {
    $result = $initialValue;

    foreach ($items as $item) {
        $result = $callback($result, $item);
    }

    return $result;
}

$totalDishesPrice = reduce(
    $dishes, 
    function ($dishesPrice, $dish) {
        return $dishesPrice + $dish->price;
    }, 
    0
);

 

 

 

 

Conclusion

 

This post is a summary of what I’ve learned from the first few chapters of the book Refactoring to Collection.

 

Reading through the book was actually a big revelation.

 

I have discovered a lot of little tricks that make code way easier to write and most importantly to read.

 

Also, and maybe most important than that, the book reminded me of how much there is still to learn in this field.

 

Developing better software, while using PHP, in particular, is a long-life process that will not end until you decide to stop.

 

There are always little tricks to discover and concept that we have never thought about even after years and years of programming in our back.

 

I hope this little introduction to collections made you want to learn more about it.

 

Ready?

 

In case you brush up some more PHP you can now go straight to the Basics of PHP,

 

If instead, you prefer to move into something else here is a very recent series I stared about Composer and its secrets.

 

 
 
If you like this content and you are hungry for some more join the Facebook's community in which we share info and news just like this one!

Other posts that might interest you

Coding (Php 7.x) Aug 19, 2020

Introduction to Composer [Command-line interface pt2]

See details
Coding (Php 7.x) Sep 19, 2020

Introduction to Composer [ the json scheme]

See details
Get my free books' review to improve your skill now!
I don't want to improve