In this article I would like to show you how you can use functional programming with JavaScript. Write your code avoiding iteration, start using map, reduce and filter in order to have less complexity and a more readable code.
When you finish reading this article, you will be ready to use JavaScript functional features.
Functional Programming
It was the first programming paradigm to be created and the last one to be adopted. In the beginning the computers did not have significant processing power, so they could not afford the fact that function calls are more expensive than simple loops. This is one of the reasons it took so long for functional programming to gain popularity.
The functional programming concept is to think how we can express the code in terms of functions, input and outputs. Instead of objects and step-by-step instructions. It also focus on being declarative and explicit, rather than imperative and encapsulated state.
Its approach is to have immutable data, so it helps you not changing things that you didn’t mean to. For example: if you want to replace an object in an array, you map through it and returns a new array. You don’t replace the object in the same array, as you may do in imperative programming. This way debugging your code is going to be easier. But, there is a downside, as things get bigger you may have efficiency problems.
Functions Are First-Class Citizens
Javascript treats functions as values, so you can assign functions, pass them as parameters and return them in functions.
let characterName = (name) => console.log(name);
characterName('Batman'); //Batman
Higher-Order Functions
Those are functions that can return a function or take other functions as arguments. This feature allow us to use function composition. So now you can replace big loops with composed functions. And, you can break the problem in small parts, solving each part with a function and those can be reused in another circumstances.
const characters = [
{ name: 'Alfred', occupation: 'Butler', villain: false},
{ name: 'Batman', occupation: 'CEO of Wayne Enterprises', villain: false},
{ name: 'Harley Quinn', occupation: 'Former psychiatrist', villain: true},
{ name: 'Joker', occupation: 'Villain', villain: true}
];
const villainName = [];
for (let character of characters) {
if (character.villain)
villainName.push(character.name);
}
console.log(villainName); // [ 'Harley Quinn', 'Joker' ]
const villain = (character) => {
return character.villain;
};
const name = (character) => {
return character.name;
};
const villainNames = characters.filter(villain).map(name);
console.log(villainNames); // [ 'Harley Quinn', 'Joker' ]
Closures
The MDN definition for closure is:
Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure ‘remembers’ the environment in which it was created.
Let’s illustrate this concept:
const characterQuote = () => {
const quote = 'I am Batman';
const display = () => {
return console.log(${quote}
);
};
display();
};
characterQuote();
Map, Reduce and Filter
Here it is a image to illustrate how this concepts work. I have all my ingredients to make a sandwich. So, I take each one at a time and chop it (map). After that, I can piece the ingredients together (reduce). And if you don't like tomato, don't let it pass (filter).
Map
Applies a function to all the array's elements and returns a new array with the returned values.
Here we use the JavaScript implementation for map.
const characters = [
{ name: 'Alfred', occupation: 'Butler'},
{ name: 'Batman', occupation: 'CEO of Wayne Enterprises'},
{ name: 'Harley Quinn', occupation: 'Former psychiatrist'},
{ name: 'Joker', occupation: 'Vilain'}
];
const occupations = characters.map((character) => {
return character.occupation;
});
console.log(occupations); // [ 'Butler', 'CEO of Wayne Enterprises', 'Former psychiatrist', 'Vilain' ]
But we could solve the same problem without using functional programming:
const characters = [
{ name: 'Alfred', occupation: 'Butler'},
{ name: 'Batman', occupation: 'CEO of Wayne Enterprises'},
{ name: 'Harley Quinn', occupation: 'Former psychiatrist'},
{ name: 'Joker', occupation: 'Vilain'}
];
const mapOccupation = (characters) => {
let occupations = [];
for (let i = 0; i < characters.length; i++) {
occupations.push(characters[i].occupation);
}
return occupations;
}
console.log(mapOccupation(characters));
When comparing both, we can realize that with functional programming our code is composable, cleaner, and the way you write your program matches with the problem you want to solve.
Reduce
Computes a single value given an array as entry. You must also inform a function and a start value.
const characters = [
{ name: 'Alfred', age: 70},
{ name: 'Batman', age: 40},
{ name: 'Harley Quinn', age: 29},
{ name: 'Joker', age: 35}
];
const totalAge = characters.reduce((sum, character) => {
return sum + character.age;
}, 0);
console.log(totalAge); // 174
Reduce not only works with numbers, but is also great when we need to boil an array into a new value. In fact, you can implement any high-order functions with reduce.
const characters = [
['Alfred', 'Butler'],
['Batman', 'CEO'],
['Harley Quinn', 'Former Psychiatrist']
];
const charactersObject = characters.reduce((object, character) => {
object[character[0]] = character[1];
return object;
}, {})
console.log(charactersObject);
/*
{
Alfred: "Butler",
Batman: "CEO",
Harley Quinn: "Former Psychiatrist"
}
*/
const characters = ['Alfred', 'Batman', 'Harley Quinn', 'Batman'];
const arrayLength = (array) => {
return array.reduce((length, value) => {
return ++length;
}, 0);
}
console.log(arrayLength(characters)); // 4
const characters = ['Alfred', 'Batman', 'Harley Quinn', 'Batman'];
const findCharacter = (array, test) => {
return array.reduce((aggr, value) => {
return aggr || test(value);
}, false);
}
console.log(findCharacter((characters), (character) => {
if (character === 'Batman') return character;
})); // Batman
Curry
Always takes one single argument and returns another function that takes one parameter until all the arguments have been applied.
const batmanAge = 31;
const jokerAge = 29;
const olderThan = (character1) => {
return (character2) => { return character2 > character1 };
}
const olderThanBatman = olderThan(batmanAge);
console.log(olderThanBatman(jokerAge)); //false
Partial Application
Partial applies one or more arguments to the function and the returned function takes the remaining parameters to complete the application.
const ageBatman = 40;
const ageJoker = 29;
const ageAlfred = 70;
const sumAges = (ageBatman, ageJoker, ageAlfred) => {
return ageBatman + ageJoker + ageAlfred;
}
const partialSum = sumAges.bind(null, ageBatman);
console.log(partialSum(ageJoker, ageAlfred)); // 139
Downside
Function calls are more expensive than simple loops. This is one of the reasons it took so long to functional programming to gain popularity, in the beginning the computers did not have significant processing power.
Conclusion
Now you have the basic to start using functional programming with JavaScript. You can become a better and more confident programmer.
It is going to be easier to decompose a big task into smaller ones making your code more reliable and predictable.
If you got interested and want to learn more here are some useful links:
The book Eloquent Javascript.
The functional programming path from MPJ.
Thanks to Guilherme Vinicius Moreira, Luiz Rogerio, Marcle Rodrigues, and Marina Limeira.
We want to work with you. Check out our "What We Do" section!