-
Notifications
You must be signed in to change notification settings - Fork 0
JavaScript High Order Functions
A function that takes another function as an argument is a high-order function.
Suppose you are in prison in North Korea, being forced to code JavaScript. Your job is to write a virus that will replace h1 headings with "Long Live Kim Jong-un!". You write this function:
var longLiveKimJongUn = function() {
.h1.inner.HTML("Long Live Kim Jong-un!");
}
But you want to escape. You set up a query on the location of the user and write a function inside the function. If the user is in North Korea the banner says "Long Live Kim Jong-un!" But if the user is in the free world the banner says "Help! I'm being held prisoner in North Korea and forced to write JavaScript!"
var longLiveKimJongUn = function(callback) {
callback();
}
var locationNorthKorea = function() {
console.log("Long Live Kim Jong-un!");
}
var locationFreeWorld = function() {
console.log("Help! I'm being held prisoner in North Korea and forced to write JavaScript!");
}
longLiveKimJongUn(locationNorthKorea);
The function longLiveKimJongUn takes the location query as its parameter. The parameter callback symbolizes or is replaced by (abstracts?) the anonymous inner functions. The anonymous inner function then prints the banner.
#Creating a forEach() Iterator
To save time writing iterators you create a function:
var forEach = function(arr, callback) {
for (var i = 0; i < arr.length; i++) {
arr[i] = callback(arr[i]);
}
return arr;
};
var arr = [1,2,3,4];
console.log (forEach(arr, function(item) { return item + 10 }));
//[11, 12, 13, 14]
The forEach() iterator replaces the input array with the manipulated output array.
There are two functions. An anonymous function is nested inside the outer forEach function. There are two return statements because there are two functions, i.e., each function must have its own return statement.
The forEach function has two parameters. arr is the array. The second argument is callback. callback abstracts (symbolizes or is replaced by) the anonymous function.
The manipulation of each element is done in the anonymous function. This enables the user to easily change the manipulation, e.g., multiple by 10 instead of add 10.
In the forEach function a for loop iterates through the array replacing each element with a manipulated element. When the for loop finishes, the manipulated array is returned.
The second return statement, in the anonymous function, is needed to return a result to the outer forEach function. Without the inner return statement the results would be [undefined, undefined, undefined, undefined].
#Creating a map() Iterator
var map = function(arr, callback) {
var results = [];
for (var i = 0; i < arr.length; i++) {
results.push(callback(arr[i]));
}
return results;
};
var arr = [1,2,3,4];
console.log(map (arr, function(item) { return item * 10 }) );
map() differs from forEach() in that it creates a second array with the results.
First, we need to declare the second array (results).
Next, callback abstracts the second (nested) function. The nested function is anonymous. You can think of its name as callback. In this case the anonymous function is item * 10, so callback becomes item * 10.
item is a parameter of the anonymous function. In the map function item is replaced by arr[i].
Then we push() the original elements, one by one, as modified by the callback function into the new array results.
Then the array results are returned.
Note the second return. There are two functions so two returns are needed. The second return assigns the value of callback(arr[i]) to function(item) {return item * 10}. In other words, the new value (ten times the old value) replaces the anonymous function.
#Creating a filter() Iterator
var filter = function(arr, callback) {
var results = [];
for (var i = 0; i < arr.length; i++) {
if (callback(arr[i]) === true) {results.push(arr[i])};
}
return results;
};
var arr = [5,10,15,20];
console.log(filter (arr, function(item) { return item > 10 }) );
This tests is each element of the array is greater than ten, which returns a boolean (true or false). If the value is true then the element is pushed to the second array results.
if (callback(arr[i]) === true) can also be written if (callback(arr[i])) (but is less readable).
#Creating a reduce() Iterator
var reduce = function(arr, callback, start) {
var reduced = 0;
for (var i = start; i < arr.length; i++) {
reduced += callback(arr[i-1], arr[i]);
}
return reduced;
};
// var arr = ["this ", "is ", "a ", "string"];
var arr = [1,2,3,4];
console.log(reduce (arr, function(prev, cur) { return prev + cur; }, 0) );
This adds all the elements of an array:
[1,2,3,4] --> 10
["this ", "is ", "a ", "string"] --> this is a string
The start (third) parameter sets the element on which the iterator starts. 1 is the default. 0 doesn't work because arr[-1] doesn't exist.
The program doesn't work perfectly with strings because 0 is concatenated to the start of the string. This could be fixed by checking for type and setting reduced as null for strings. There might be a better way to solve this problem.
The += operator adds each pass to the previous passes. However, this operator doesn't work if the variable reduced starts as null or undefined. Setting this variable at 0 fixes this problem.