Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[JavaScript] Functional Programming

These are my notes while doing the course JavaScript Algorithms and Data Structures on https://www.freecodecamp.org. I highly recommend it if you prefer to try things directly rather than watching videos.


Functional programming follows a few core principles:

  • Functions are independent from the state of the program or global variables. They only depend on the arguments passed into them to make a calculation
  • Functions try to limit any changes to the state of the program and avoid changes to the global objects holding data
  • Functions have minimal side effects in the program

Callbacks are the functions that are slipped or passed into another function to decide the invocation of that function. You may have seen them passed to other methods, for example in filter, the callback function tells JavaScript the criteria for how to filter an array.

Functions that can be assigned to a variable, passed into another function, or returned from another function just like any other normal value, are called first class functions. In JavaScript, all functions are first class functions.

The functions that take a function as an argument, or return a function as a return value are called higher order functions.

When the functions are passed in to another function or returned from another function, then those functions which gets passed in or returned can be called a lambda.


Pass Arguments to Avoid External Dependence in a Function

Example: Adding/Removing a book to/from a bookList without changing the global bookList array. Instead it returns a new array.

var bookList = ["The Hound of the Baskervilles", "On The Electrodynamics of Moving Bodies", "Philosophiæ Naturalis Principia Mathematica", "Disquisitiones Arithmeticae"];

/* This function should add a book to the list and return the list */
function add(list, bookName) {
  return [...list, bookName];
}

/* This function should remove a book from the list and return the list */
function remove(list, bookName) {
  return list.filter(book => book !== bookName);
}

var newBookList = add(bookList, 'A Brief History of Time');
var newerBookList = remove(bookList, 'On The Electrodynamics of Moving Bodies');
console.log(bookList); // still contains the same books

The remove function could also look like this:

function remove(arr, bookName) {
  let newArr = [...arr]; 
  if (newArr.indexOf(bookName) >= 0) {
    newArr.splice(newArr.indexOf(bookName), 1);
    return newArr; 
  }
}

The methods map(), filter(), slice(), concat() and reduce() are pure functions, as their output depends solely on its inputs and does not mutate the original array.

map() 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.

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

const names = users.map(user => user.name); // [ 'John', 'Amy', 'camperCat' ]

A map() implementation could look like this:

var s = [23, 65, 98, 5]; // global Array

Array.prototype.myMap = function(callback) {
  var newArray = [];
  for (let i = 0; i < this.length; i++) {
    newArray.push(callback(this[i]));
  }
  return newArray;
};

var new_s = s.myMap(function(item) {
  return item * 2;
});

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.
The callback function accepts 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 filter method was called.

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

const usersUnder30 = users.filter(user => user.age < 30); // [ { name: 'Amy', age: 20 }, { name: 'camperCat', age: 10 } ]

A filter() implementation could look like this:

var s = [23, 65, 98, 5]; // global Array

Array.prototype.myFilter = function(callback) {
  var newArray = [];
    for (let i = 0; i < this.length; i++) {
      if (callback(this[i])) {
        newArray.push(this[i]);
      }
  }
  return newArray;
};

var new_s = s.myFilter(function(item) {
  return item % 2 === 1;
});

slice() returns a copy of certain elements of an array.

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

concat() combines arrays into a new one without mutating the original arrays.

[1, 2, 3].concat([4, 5, 6]); // Returns a new array [1, 2, 3, 4, 5, 6]

reduce() iterates over each item in an array and returns a single value (i.e. string, number, object, array) via a callback function that is called on each iteration.

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

const usersObj = users.reduce((obj, user) => {
  obj[user.name] = user.age;
  return obj;
}, {});
console.log(usersObj); // { John: 34, Amy: 20, Max: 10 }