The Absence of Side Effects™.
Functional Functions (FF) don't rely on data outside the function, and doesn't mutate data that exists outside the current function. (immutable data)
Unfunctional Function (UF):
var a = 0
function increment1(){
a++
}
Functional Function (FF):
function increment(a){
return a+1 // returns a new copy incremented by 1
}
Classic UF way to loop over items in an array:
var names = ['Matt', 'Jesse', 'Brian']
// classic for loop
for(var i = 0; i < names.length; i++){
console.log(names[i])
}
Slightly more FF way:
var names = ['Matt', 'Jesse', 'Brian']
// classic for loop
names.forEach(function(v, i){
console.log(v)
})
Map takes a function and a collection of items.
It makes a new collection, and passes each item in the old collection to the function, and inserts the return value into the new collection.
Example 1, "map words to lengths":
function length(v){
return v.length
}
['Matt', 'Jesse', 'Brian'].map(length)
// --> [4, 5, 5]
Example 2, "square every number":
function sq(v){
return v*v
}
[1, 2, 3, 4, 5].map(sq)
// --> [1, 4, 9, 16, 25]
In languages that support higher order functions, like JavaScript, we can use anonymous functions:
[1, 2, 3, 4, 5].map(function(v){
return v*v
})
// --> [1, 4, 9, 16, 25]
In ES6 this can be even more terse:
[1, 2, 3, 4, 5].map((v) => v*v)
// --> [1, 4, 9, 16, 25]
The following examples will use rand(a,b), which returns a random integer between a and b. Here's the code for rand():
function rand(a,b){
var diff = b-a+1
return Math.floor(Math.random()*diff) + a
}
Classic UF way to loop over and replace items in an array with random code names:
var names = ['Matt', 'Jesse', 'Brian']
var code_names = ['The Wo', 'Darth Lincoln', 'The Distiller']
for(var i = 0; i < names.length; i++){
names[i] = code_names[rand(0, names.length)]
}
The FP way:
var names = ['Matt', 'Jesse', 'Brian']
var code_names = ['The Wo', 'Darth Lincoln', 'The Distiller']
names = names.map(function(v, i, arr){
return code_names[rand(0, arr.length)]
})
Like Map, Reduce takes a function and a collection of items. It returns a value that is created by combining the items.
Example 1, "get a sum of a collection":
[1, 2, 3, 4, 5].reduce(function(a, v){
return a + v
}, 0)
// --> 15
In the example above, v represents each item in the array, a represents the accumulator. The accumulator is given the value 0 to start.
In ES6 this can be even more terse:
[1, 2, 3, 4, 5].reduce((a, v) => a + v, 0)
This can easily be combined with Map, for instance, to get the combined length of all names in a collection.
Example 2, "get combined length of all names":
['Matt', 'Jesse', 'Brian'].map(length).reduce(function(a, v){
return a + v
}, 0)
// --> 14
In ES6 this can be even more terse:
['Matt', 'Jesse', 'Brian'].map(length).reduce((a, v) => a + v, 0)
// --> 14
Example 3, "count occurence of 'he' in a book passage":
var passage = "He ran a few miles before coming to the edge of a field, where he
rested upon a great, gnarled oak tree the age of Earth itself."
passage
.toLowerCase()
.replace(/,/g, '')
.split(' ')
.reduce(function(a, v){
return a + (v.indexOf('he') !== -1 ? 1 : 0)
}, 0)
// --> 5
And again, in ES6 this can be even more terse:
passage
.toLowerCase()
.replace(/,/g, '')
.split(' ')
.reduce((a, v) => a + (v.indexOf('he') !== -1 ? 1 : 0), 0)
// --> 5
Filter is built-in to Arrays for ES5+ as well, and it also takes a function and collection of items. Filter creates a new collection of items. It passes each item in the old collection to the function. If the function returns a "truthy" value, that item is inserted into the new collection.
function len5(v){
return v.length >= 5
}
['Matt', 'Jesse', 'Brian'].filter(len5)
// --> ['Jesse', 'Brian']
In ES6 this can be even more terse:
['Matt', 'Jesse', 'Brian'].filter((v) => v.length >= 5)
// --> ['Jesse', 'Brian']