Functional Array Operations in JavaScript
JavaScript's Array prototype provides a comprehensive selection of methods which lend themselves nicely to functional programming approach, such as map
and reduce
. Surprisingly, when doing code reviews I often see that developers are not aware of them or not used to using them. In this post, I'll do an overview of these functions with examples how they can simplify your code.
Find
The find
method will return the first element in the array, which matches the supplied predicate function, or undefined
if no such element exists.
It can be used to simplify code like this:
for (let i = 0; i < array.length; i++) {
if (array[i].id === selectedId) {
return array[i];
}
}
return undefined;
The functional equivalent is a one-liner:
return array.find(function(element) {
return element.id === selectedId;
});
For .NET developers, this method behaves the same as FirstOrDefault in LINQ.
Filter
A very similar method is filter
. It will return all the matching elements instead of just the first one.
It can be used in the following situation:
let completedElements = [];
for (let i = 0; i < array.length; i++) {
if (array[i].status === 'complete') {
completedElements.push(array[i]);
}
}
We can achieve the same using the following:
let completedElements = array.filter(function(element) {
return element.status === 'complete';
});
The LINQ equivalent is Where.
Map
When you want to transform or project the elements in the array, you can use map
.
The non-functional sample to simplify:
let elementsWithDistance = []
for (let i = 0; i < array.length; i++) {
let elementWithDistance = {
id: array[i].id,
name: array[i].name,
distance: calculateDistance(currentLocation, array[i].location)
};
elementsWithDistance.push(elementWithDistance);
}
Equivalent code using map
:
let elementsWithDistance = array.map(function(element) {
return {
id: element.id,
name: element.name,
distance: calculateDistance(currentLocation, element.location)
}
});
In LINQ, the same can be achieved with Select.
Some
To check whether any items in the array match a condition, some
can be used.
We could hand-code it like this:
let contains = false;
for (let i = 0; i < array.length; i++) {
if (array[i].id === selectedId) {
contains = true;
}
}
Or just use some
instead:
let contains = array.some(function(element) {
return element.id === selectedId;
});
In LINQ, Any does the same.
Every
When we want to make sure that all elements meet a condition, we will use every
.
We could achieve that with the following code:
let done = true;
for (let i = 0; i < array.length; i++) {
if (array[i].status !== 'complete') {
done = false;
}
}
Using every
instead would be simpler:
let done = array.every(function(element) { element.status === 'complete' });
All
can be used in LINQ in such cases.
Reduce
The reduce
function is useful when we want to aggregate all of the array into a single value, e.g. we are looking for the smallest element.
This is how we could find the closest element:
let closestElement = undefined;
for (let i = 0; i < array.length; i++) {
if (closestElement === undefined || array[i].distance < closestElement.distance) {
closestElement = array[i];
}
}
Using reduce
in such cases might seem a little unintuitive until you get used to it:
let closestElement = array.reduce(function(a, b) {
return a !== undefined && a.distance <= b.distance ? a : b;
}, undefined);
The corresponding function in LINQ is called Aggregate.
You should check, in which browsers each method is supported before you start using it. In same cases you might need to use polyfills or a transpiler.