Advantages of using Higher Order Functions

Let’s take a sample:

const has = p => o => o.hasOwnProperty(p);
const sortBy = p => (a, b) => a[p] > b[p];

let result;
let users = [
  { name: 'Qian', age: 27, pets : ['Bao'], title : 'Consultant' },
  { name: 'Zeynep', age: 19, pets : ['Civelek', 'Muazzam'] },
  { name: 'Yael', age: 52, title : 'VP of Engineering'}
];

result = users
  .filter(has('pets'))
  .sort(sortBy('age'));

What’s going on here? We’re calling the Array prototype’s sort and filter methods, each of which take a single function argument, but instead of writing function expressions and passing them to filter and sort, we’re calling functions that return functions, and passing those to filter and sort.

Let’s take a look, with the expression that returns a function underlined in each case.

Without higher order functions:

result = users
  .filter(x => x.hasOwnProperty('pets')) //pass Function to filter
  .sort((a, b) => a.age > b.age);        //pass Function to sort

With higher order functions:

result = users
  .filter(has('pets'))  //pass Function to filter
  .sort(sortBy('age')); //pass Function to sort

In each case, filter is passed a function that checks if an object has a property called “pets.”

Why is this useful?

This is useful for a few reasons:

  • It reduces repetitive code
  • It allows for easier reuse of code
  • It increases clarity of code meaning

Imagine we want only users with pets and with titles. We could add another function in:

result = users
  .filter(x => x.hasOwnProperty('pets'))
  .filter(x => x.hasOwnProperty('title'))
  ...

The repetition here is just clutter: it doesn’t add clarity, it’s just more to read and write. Compare with the same code using our has function:

result = users
  .filter(has('pets'))
  .filter(has('title'))
  ...

This is shorter and easier to write, and that makes for fewer typos. I consider this code to have greater clarity as well, as it’s easy to understand its purpose at a glance.

As for reuse, if you have to filter to pet users or people with job titles in many places, you can create function to do this and reuse them as needed:

const hasPets = has('pets');
const isEmployed = has('title');
const byAge = sortBy('age');

let workers = users.filter(isEmployed);
let petOwningWorkers = workers.filter(hasPets);
let workersByAge = workers.sort(byAge);

We can use some of our functions for single values as well, not just for filtering arrays:

let user = {name: 'Assata', age: 68, title: 'VP of Operations'};
if(isEmployed(user)){   // true
  //do employee action
}
hasPets(user);          // false
has('age')(user);       //true

A Step Further

Let’s make a function that will produce a filter function that checks that an object has a key with a certain value. Our `has` function checked for a key, but to check value as well our filter function will need to know two things (key and value), not just one. Let’s take a look at one approach:

//[p]roperty, [v]alue, [o]bject:
const is = p => v => o => o.hasOwnProperty(p) && o[p] == v;

// broken down:
// outer:  p => [inner1 function, uses p]
// inner1: v => [inner2 function, uses p and v]
// inner2: o => o.hasOwnProperty(p) && o[p] = v;

So our new function called “is” does three things:

  1. Takes a property name and returns a function that…
  2. Takes a value and returns a function that…
  3. Takes an object and tests whether the object has the property specified with the value specified, finally returning a boolean.

Here is an example of using this `is` to filter our users:

const titleIs = is('title');
// titleIs == v => o => o.hasOwnProperty('title') && o['title'] == v;

const isContractor = titleIs('Contractor');
// isContractor == o => o.hasOwnProperty('contractor') && o['title'] == 'Contractor';

let contractors = users.filter(isContractor);
let developers  = users.filter(titleIs('Developer'));

let user = {name: 'Viola', age: 50, title: 'Actress', pets: ['Zak']};
isEmployed(user);   // true
isContractor(user); // false

A note on style

Scan this function, and note the time it takes you to figure out what’s going on:

const i = x => y => z => h(x)(y) && y[x] == z;

Now take a look at this same function, written slightly differently:

const is = prop => val => obj => has(prop)(obj) && obj[prop] == val;

There is a tendency when writing one line functions to be as terse as possible, at the expense of readability. Fight this urge! Short, meaningless names make for cute-looking, hard to understand functions. Do yourself and your fellow coders a favor and spend the extra few characters for meaningful variable and function names.

One more thing. . .

What if you want to sort by age in descending order rather than ascending? Or find out who’s not an employee? Do we have to write new utility functions `sortByDesc` and `notHas`? No we do not! We can wrap our functions, which return Booleans, with a function that inverts that boolean, true to false and vice versa:

//take args, pass them thru to function x, invert the result of x
const invert = x => (...args) => !x(...args);
const noPets = invert(hasPets);

let petlessUsersOldestFirst = users
  .filter(noPets)
  .sort(invert(sortBy('age')));

ref: https://strongloop.com/strongblog/higher-order-functions-in-es6easy-as-a-b-c/
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s