Introduction
Functional Programming lies somewhere in the middle-ground between Computer Science and Mathematics. This is a pretty dense and theoric subject, so my goal here is to show you how to start using right away some classic functions for collections manipulation in your everyday code, focusing in practical examples of when you might use them.
In this post I'll show you how to use the 7 classical functions that I find most useful in collections manipulation:
Using these simple functions you'll find yourself writting a lot less of boilerplate code. Kiss goodbye a lot of for
loops and if
tests. Focus your code on your business logic, not on collection traversal and array accessing.
each
You've been there. You have a collection of things, and you want to do something with each
one of them.
Use case: I want set each one of my orders as finished.
for (var i = 0; i < orders.length; i++) {
orders[i].finished = true;
}
Basic implementation: each
is a function that operates on a collection. For each item of this collection, a function will be called, receiving this item as a parameter. Inside this function, you do whatever you want with that item.
var each = function(list, operation) {
for (var i = 0; i < list.length; i++) {
operation(list[i]);
}
}
Rewriting the example using each
:
each(orders, function(order) {
order.finished = true;
});
We don't worry about collection traversal or accessing elements using indexes. We just worry about what we want to do for each item.
filter
You have a collection of things, and you want a collection with only the things that have some characteristic of your interest.
Use case: I want a list with the orders of the customer with code 1.
var ordersCustomer1 = [];
for (var i = 0; i < orders.length; i++) {
if (orders[i].customer === 1) {
ordersCustomer1.push(orders[i]);
}
}
Basic implementation: filter
is a function that, for each item of a collection, calls a function passing that item as a parameter. This function should use that item to test some characteristic of this item (a predicate), and return a boolean that indicates whether it has or has not that characteristic. All items that have the characteristic will be accumulated in a new collection and returned by filter
.
var filter = function(list, operation) {
var results = []
for (var i = 0; i < list.length; i++) {
if (operation(list[i])) {
results.push(list[i]);
}
}
return results;
}
Rewriting the example using filter
:
var ordersCustomer1 = filter(orders, function(order) {
return order.customer === 1;
});
find
find
is a filter
with 2 differences: you filter using a unique identifier, and it returns a single item (not a collection).
Use case: I want to find the order with code 2.
var order2;
for (var i = 0; i < orders.length; i++) {
if (orders[i].number === 2) {
order2 = orders[i];
break;
}
}
Basic implementation: find
is a function that, for each item of a collection, calls a function passing that item as a parameter. This function should use that item to test some unique identifier of this item (a predicate), and return a boolean that indicates whether it has or has not that identifier. find
will stop searching as soon as it finds the first item that matches the predicate and will return this item.
var find = function(list, operation) {
for (var i = 0; i < list.length; i++) {
if (operation(list[i])) {
return list[i];
}
}
}
Rewriting the example using find
:
var order2 = find(orders, function(order) {
return order.number === 2;
});
some
You want to know if some
of your items has some characteristic. It can be only one, two, or it can be all of them.
Use case: I want to know whether exists a not finished order.
var existsNotFinished = false;
for (var i = 0; i < orders.length; i++) {
if (!orders[i].finished) {
existsNotFinished = true;
break;
}
}
Basic implementation: some
is a function that, for each item of a collection, calls a function passing that item as a parameter. This function should use that item to return a boolean value that indicates whether the item has the characteristic. some
will return true
as soon as it finds some item that has the characteristic, of false
if none does.
var some = function(list, operation) {
for (var i = 0; i < list.length; i++) {
if (operation(list[i])) {
return true;
}
}
return false;
}
Rewriting the example using some
:
var existsNotFinished = some(orders, function(order) {
return !order.finished;
});
every
You want to know if every
of your items has some characteristic.
Use case: I want to know whether all orders are not finished.
var allFinished = true;
for (var i = 0; i < orders.length; i++) {
if (!orders[i].finished) {
allFinished = false;
break;
}
}
Basic implementation: every
is a function that, for each item of a collection, calls a function passing that item as a parameter. This function should use that item to return a boolean value that indicates whether the item has the characteristic. every
will return false
as soon as it finds some item that doesn't have the characteristic, of true
if all of them do.
var every = function(list, operation) {
for (var i = 0; i < list.length; i++) {
if (!operation(list[i])) {
return false;
}
}
return true;
}
Rewriting the example using every
:
var allFinished = every(orders, function(order) {
return order.finished;
});
map
For each item of your collection you want to obtain a value by transforming that item, and you want a collection of these values back.
Use case: I need a list containing only the numbers of my orders (I want to transform my orders into its numbers).
var orderNumbers = [];
for (var i = 0; i < orders.length; i++) {
orderNumbers.push(orders[i].number);
}
Basic implementation: map
is a function that, for each item of a collection, calls a function passing that item as a parameter. This function should use that item to obtain some value from it (getting one of its properties, making some kind of calculation etc) and return this value. map
will accumulate those values in a collection and return this collection.
var map = function(list, operation) {
var results = [];
for (var i = 0; i < list.length; i++) {
results.push(operation(list[i]));
}
return results;
}
Rewriting the example using map
:
var orderNumbers = map(orders, function(order) {
return order.number;
});
reduce
From your collection, you want to get to some value that somehow represents it, like some kind of totalization.
Use case: I want to know the total cost of my order.
var orderTotal = 0;
var order = orders[0]
for (var i = 0; i < order.items.length; i++) {
var item = order.items[i];
orderTotal += item.value * item.quantity;
}
Basic implementation: reduce
is a function that, for each item of a collection, calls a function passing that item and an accumulator value as parameters. This function should use that item and the accumulator to calculate some value, that will be the new value of the accumulator. reduce
receives an initial accumulator value, that for addition will be usually 0
.
var reduce = function(list, operation, initial) {
var accumulator = initial;
for (var i = 0; i < list.length; i++) {
accumulator = operation(accumulator, list[i]);
}
return accumulator;
}
Rewriting the example using reduce
:
var orderTotal = reduce(orders[0].items, function(acc, item) {
return acc + (item.value * item.quantity);
}, 0)
Combining functions
I want to show you an example of how these functions can be combined to make your code more concise and expressive. Suppose we want to obtain a list with the total value of each of the orders. We could do this:
var totals = [];
for (var i = 0; i < orders.length; i++) {
var order = orders[i];
var total = 0;
for (var j = 0; j < order.items.length; j++) {
var item = order.items[j];
total += item.value * item.quantity;
}
totals.push(total);
}
Generically, what we want to do is to map
each order to a function that will reduce
the value of its items multiplied by the quantities into a single value:
var totals = map(orders, function(order) {
return reduce(order.items, function(acc, item) {
return acc + (item.quantity * item.value);
}, 0);
});
Further reading
If you want some nice reading about Functional Programming, check out Professor Frisby's Mostly Adequate Guide to Functional Programming.
Some video courses on the subject: Programming Languages and Functional Programming Principles in Scala.
I also recommend you to read some real world implementations of the functions explained here in Underscore.js source code.