A reminder for functional programming in javascript.

Also, here is a good short comparison of functional and object-oriented programming.
https://www.baeldung.com/cs/oop-vs-functional

Core principles

Don’t change things – write pure functions

One of the core principles of functional programming is to not change things. Changes lead to bugs. It’s easier to prevent bugs knowing that our functions don’t change anything, including the function arguments or any global variable.

In functional programming, changing or altering things is called mutation, and the outcome is called a side effect. A function, ideally, should be a pure function, meaning that it does not cause any side effects.

For example, using .splice() method on an array mutates the original array, so that any subsequent operation on the array sees a mutated array which can cause unexpected outcome.
The solution is not to use such methods and pay attention that we do not change the original data.

var Window = function(tabs) {
  this.tabs = tabs; // We keep a record of the array inside the object
};

// Close a tab
Window.prototype.tabClose = function (index) {
  this.tabs.splice(index, 1); 
  // removes a specific tab from tabs array, but mutates it
  return this;
 };

Using var newArr = arrVar will simply create a reference to the existing array and not a copy. So changing a value in newArr would change the value in arrVar, and this is not what we want.

var arrVar = [1,2,3];

function add() {
  var badArr = arrVar; // creates a reference, not a copy!
  var goodArr = [...arrVar]; // creates a copy using spread operator
}

Declare dependencies explicitly

Another principle of functional programming is to always declare your dependencies explicitly. This means if a function depends on a variable or object being present, then pass that variable or object directly into the function as an argument, instead of relying on a global variable being available.

There are several good consequences from this principle. The function is easier to test, we know exactly what input it takes, and it won’t depend on anything else in our program. This can give us more confidence when we alter, remove, or add new code.

Finally, the function would always produce the same output for the same set of inputs, no matter what part of the code executes it.

// The global variable
var fixedValue = 4;

// Relying on global variable
function incrementer () {
  return fixedValue + 1;
}

// Declaring dependency explicitly
function incrementer (value) {
  return value + 1;
}

Use map() to extract values from array

Array.prototype.map(), or more simply map, is a method on the array object prototype. It iterates over each item in an array and returns a new array containing the results of calling the callback function on each element. It does this without mutating the original array.

When the callback is used, it is passed three arguments. The first argument is the current element being processed. The second is the index of that element and the third is the array upon which the map method was called.

So map() is a pure function – as long as its callback function is also a pure function.

const users = [
  { name: 'John', age: 34, 'email': 'john@gmail.com' },
  { name: 'Amy', age: 20, 'email': 'amy@gmail.com' },
  { name: 'Ginger', age: 16, 'email': 'ginger@gmail.com' }
];

const names = users.map(item => ({
  name: item.name,
  email: item['email']
}));
// To avoid confusion we used "item" instead of "user"
console.log(names);

Create a custom map method on Array.prototype

For better understanding of map and similar array methods, let’s consider writing our own Array.prototype method which iterates over an array and returns a new array of the same length, with a callback function that also doesn’t mutate the original array.

var numList = [23, 65, 98, 5];

Array.prototype.myMap = function(callback) {
  var newArray = [];
  this.forEach(item => newArray.push(callback(item)));
  return newArray;
};

var numDouble = numList.myMap(function(item) {
  return item * 2;
});
// returns [46, 130, 196, 10]

Use filter() to extract data from array

filter calls a function on each element of an array and returns a new array containing only the elements for which that function returns true. In other words, it filters the array, based on the function passed to it. Like map, it does this without needing to modify the original array.

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'Ginger', age: 16 }
];

const usersUnder30 = users.filter(user => user.age < 30);
console.log(usersUnder30); // returns an array with Amy and Ginger objects inside

Create a custom filter method on Array.prototype

For better understanding of filter method, let’s consider writing our own Array.prototype method which iterates over an array and returns a new array with only the values that satisfy our condition.

var numList = [23, 65, 98, 5];

Array.prototype.myFilter = function(callback) {
  var newArray = [];
  this.forEach(item => {
    if(callback(item)) {
      newArray.push(item);
    }
  })
  return newArray;
};

var newNumList = numList.myFilter(function(item) {
  return item % 2 === 1;
});
// returns [23, 65, 5]

Use slice() to return part of an array

The slice method returns a copy of certain elements of an array. It can take two arguments, the first gives the index of where to begin the slice, the second is the index for where to end the slice (and it’s non-inclusive). If the arguments are not provided, the default is to start at the beginning of the array through the end, which is an easy way to make a copy of the entire array.

The slice method does not mutate the original array, but returns a new one. Don’t confuse this with splice method which mutates the original array.

var arr = ["Cat", "Dog", "Tiger", "Zebra"];
var newArray = arr.slice(1, 3);
// returns ["Dog", "Tiger"]

Use concat() to combine multiple arrays

Concatenation means to join items end to end. JavaScript offers the concat method for both strings and arrays that work in the same way. For arrays, the method is called on one, then another array is provided as the argument to concat, which is added to the end of the first array. It returns a new array and does not mutate either of the original arrays.

Concat can also be used instead of push to combine arrays without mutating the original.

[1, 2, 3].concat([4, 5, 6]);
// returns [1, 2, 3, 4, 5, 6]

function nonMutatingConcat(original, attach) {
  return original.concat(attach);
}

Use reduce() to analyze array data

The reduce method allows for more general forms of array processing, and it’s possible to show that both filter and map can be derived as special applications of reduce. The reduce method iterates over each item in an array and returns a single value (i.e. string, number, object, array). This is achieved via a callback function that is called on each iteration.

It accepts an extra argument called the accumulator, which gets assigned the return value of the callback function from the previous iteration (basically tells the method the initial value). If this parameter is not used, then the first iteration is skipped and the second iteration gets passed the first element of the array as the accumulator.

const users = [
  { name: 'John', age: 34 },
  { name: 'Amy', age: 20 },
  { name: 'camperCat', age: 10 }
];

const sumOfAges = users.reduce((sum, user) => sum + user.age, 0);
console.log(sumOfAges);
// returns 64

Example: using filter and map to return squares of only positive integers

This is an example of using filter and map to return squares of only positive integers from the array passed to our function. The elements need to be filtered from negatives and non-integers (decimals), and then squared. Here we will first define helper functions to keep the code clean and readable, and this is also a good practice as we can reuse the helper functions.

const squareList = arr => {

  // filter only positives and integers
  const isPositiveInt = (x) => {
    return x > 0 && x % 1 == 0;
  }
  // square number
  const square = (x) => {
    return x *= x;
  }

  return arr.filter(isPositiveInt).map(square);
};

const squaredIntegers = squareList([-3, 4.8, 5, 3, -3.2]);
console.log(squaredIntegers);
// returns [25, 9] which are squares of 5 and 3

Use sort() to sort array

The sort method sorts the elements of an array according to the callback function.
sort mutates the original array so it should be used in combination with .slice or .concat to provide a new array.

JavaScript’s default sorting method is by string Unicode point value, which may return unexpected results. Therefore, it is encouraged to provide a callback function to specify how to sort the array items. When such a callback function, normally called compareFunction, is supplied, the array elements are sorted according to the return value of the compareFunction:
If compareFunction(a,b) returns a value less than 0 for two elements a and b, then a will come before b.
If compareFunction(a,b) returns a value greater than 0 for two elements a and b, then b will come before a.
If compareFunction(a,b) returns a value equal to 0 for two elements a and b, then a and b will remain unchanged.

var globalArray = [5, 6, 3, 2, 9];

function nonMutatingSort(arr) {
  // helper function
  const orderAscending = (a, b) => {
    return a === b ? 0 : a < b ? -1: 1;
  }

  return arr.slice().sort(orderAscending);
}
nonMutatingSort(globalArray);
// returns [2, 3, 5, 6, 9]

Use split() to split a String into an Array

The split method takes an argument by which to split the string, such as a space(” “), empty string (“”) or a regex (/\W+/). Since strings are immutable, this method provides a way to manipulate them.

function splitify(str) {
  splitStr = str.split(" ");
  // returns ["Hello", "World,I-am", "code"]

  splitStr = str.split(/\W+/); // "\W+" regex matches any non-word characters
  // returns ["Hello", "World", "I", "am", "code"]
}

Use join() to combine an Array into a String

The join method is used to join the elements of an array together to create a string. It takes an argument for the delimiter that is used to separate the array elements in the string.

function sentensify(str) {
  let splitStr = str.split(/\W+/);
  return splitStr.join(" ");
}
sentensify("May-the-force-be-with-you");
sentensify("May.the-force/be,with-you");
// all return "May the force be with you"

Use trim() to trim whitespace

The trim method is used to remove unneeded whitespace from both sides of a string.

function getSlug(str) {
  return str.toLowerCase()
  .trim()
  .split(/\W+/);
  .join("-");
}
getSlug("  Winter is   coming ");
// returns "winter-is-coming"

Use every(), some() methods to check Array

The every method works with arrays to check if every element passes a particular test. It returns a Boolean value – true if all values meet the criteria, false if not.

The some method works with arrays to check if any element passes a particular test. It returns a Boolean value – true if any of the values meet the criteria, false if not.

function everyPositive(arr) {
  return arr.every(item => item >= 0)
}
everyPositive([1, 2, 3, -4, 5]);
// returns false

function somePositive(arr) {
  return arr.some(item => item >= 0)
}
somePositive([1, 2, 3, -4, 5]);
// returns true

Currying

…in progress

Leave a Reply

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