ES6 Functional Examples
May 14, 2018

ES6 Functional Examples

Posted on May 14, 2018

In most examples I see for functional Javascript, I see simplistic examples of add1, time3 and addSuffix functions that work on a lone object. I think a more practical example that would show how to use functional patterns is with more practical real-life data: arrays of objects.

Let’s use this array of objects for our examples:

let people = [
  { name: 'Bob', age: 28, child: { name: 'Sally', age: 8, grade: 3 } },
  { name: 'Sara', age: 46, child: { name: 'Lois', age: 14, grade: 9 } },
  { name: 'Ann', age: 27, child: { name: 'George', age: 9, grade: 4 } },
  { name: 'Dave', age: 52, child: { name: 'Hale', age: 17, grade: 11 } },
];

Modify

If we wanted to add a year to each person, we could write an add1 function that would be applied to increase the age field. We could compose it with an upAge function that will look for field labeled age and then add 1 to it.

const add1 = (x) => x + 1;
const toFieldApplyFunc = (field, fn) =>  {
  return (obj) => {
    let y = fn(obj[field]);
    obj[field] = y;
    return obj;
  }
};
const upAge = toFieldApplyFunc('age', add1);

let upAgedPeople = people.map(upAge);

//produces upAgedPeople
[ { name: 'Bob', age: 29, child: { name: 'Sally', age: 8, grade: 3 } },
  { name: 'Sara', age: 47, child: { name: 'Lois', age: 14, grade: 9 } },
  { name: 'Ann', age: 28, child: { name: 'George', age: 9, grade: 4 } },
  { name: 'Dave', age: 53, child: { name: 'Hale', age: 17, grade: 11 } } ]

This produces an increase in the parent’s age, but of course we ask “What about the children?” We can now compose more functions and expand the map method. We will increase their age and grade by a year (we are assuming they all passed!)

const upGrade = toFieldApplyFunc('grade', add1);

let nextYearPeople = people.map((x) => {
  x = upAge(x);
  x.child = upAge(x.child);
  x.child = upGrade(x.child);
  return x;  
});

//produces nextYearPeople
[ { name: 'Bob', age: 29, child: { name: 'Sally', age: 9, grade: 4 } },
  { name: 'Sara', age: 47, child: { name: 'Lois', age: 15, grade: 10 } },
  { name: 'Ann', age: 28, child: { name: 'George', age: 10, grade: 5 } },
  { name: 'Dave', age: 53, child: { name: 'Hale', age: 18, grade: 12 } } ]

Sort

To continue our example, we will sort the array of people by age. For Arrays in Javascript, there is a .sort() method we can chain to other methods called by the array. The sort() method take a function that compares two elements in the array. While we are at it, we will refactor the map() method too, to make it easier to read.

const addAYear = (x) => {
  x = upAge(x);
  x.child = upAge(x.child);
  x.child = upGrade(x.child);
  return x;    
}

let nextYearPeople = people.map(addAYear).sort((a, b) => a.age > b.age);

// produces
[ { name: 'Ann', age: 28, child: { name: 'George', age: 10, grade: 5 } },
  { name: 'Bob', age: 29, child: { name: 'Sally', age: 9, grade: 4 } },
  { name: 'Sara', age: 47, child: { name: 'Lois', age: 15, grade: 10 } },
  { name: 'Dave', age: 53, child: { name: 'Hale', age: 18, grade: 12 } } ]

Now, let’s try to sort by the grade level of the children. To create a sort function by the grade field, we have to compose a function that can look inside the child objects, then use those values in our compare function for the sort.

getByKey() is a function that will fetch us the value for a specified key. The first time we call it getByKey('child') it will return the child object. We will need to curry that output to getByKey('age') to get the age value. We will combine both functions to our byChildAge() function with our composeLTR() function.

The composeLTR() function will take any number functions as parameters, then apply them ‘Left To Right’. In other languages and libraries, this is also called a pipe() function, flow() function or andThen function. In a pure functional concept, it’s the reverse order of compose().

With the functions we created, we can create the descendByChildAge() function that can be used by the sort() method. The function will sort the array by the children’s age in descending order, oldest to youngest.

const getByKey = (key) => (obj) => obj[key];
const composeLTR = (...fns) => (val) => fns.reduce((prev, fn) => fn(prev), val);
const createDecender = (fn) => (a, b) => fn(a) < fn(b);

const byChildAge = composeLTR(getByKey('child'), getByKey('age'));
const descendByChildAge = createDecender(byChildAge);

let nextYearPeopleByOldestChild = people.map(addAYear).sort(descendByChildAge);

Let’s add a filter, now our script will report of all people over 40 and what there age and children’s age and grade will look like a year from now.

let myPeopleReport = people.filter(p => p.age > 40).map(addAYear).sort(descendByChildAge);
console.log(myPeopleReport);

Gets this output:

[ { name: 'Dave', age: 53, child: { name: 'Hale', age: 17, grade: 12 } },
  { name: 'Sara', age: 47, child: { name: 'Lois', age: 14, grade: 10 } } ]

Source code

And here is the file my-report.js you can run with node CLI or ES6 console:

let people = [
  { name: 'Bob', age: 28, child: { name: 'Sally', age: 8, grade: 3 } },
  { name: 'Sara', age: 46, child: { name: 'Lois', age: 14, grade: 9 } },
  { name: 'Ann', age: 27, child: { name: 'George', age: 9, grade: 4 } },
  { name: 'Dave', age: 52, child: { name: 'Hale', age: 17, grade: 11 } },
];

const add1 = (x) => x + 1;
const toFieldApplyFunc = (field, fn) =>  {
  return (obj) => {
    let y = fn(obj[field]);
    obj[field] = y;
    return obj;
  }
};

const upAge = toFieldApplyFunc('age', add1);
const upGrade = toFieldApplyFunc('grade', add1);

const addAYear = (x) => {
  x = upAge(x);
  x.child = upAge(x.child);
  x.child = upGrade(x.child);
  return x;
}

const getByKey = (key) => (obj) => obj[key];
const composeLTR = (...fns) => (val) => fns.reduce((prev, fn) => fn(prev), val);
const createDecender = (fn) => (a, b) => fn(a) < fn(b);

const byChildAge = composeLTR(getByKey('child'), getByKey('age'));
const descendByChildAge = createDecender(byChildAge);

let myPeopleReport = people.filter(p => p.age > 40).map(addAYear).sort(descendByChildAge);
console.log(myPeopleReport);