So let's start with a sum:
const arr = [1,2,3,4,5,6,7,8,9,10];
let sum = 0;
for(let i = 0 ; i < arr.length ; i++){
sum = sum + arr[i];
}
This is the loop/imperative way of doing things.
We repeat a lot of things: "for", "let", "i = 0", "i < arr.length", ...
And everytime we write stuff, we risk introducing errors.
So let's see whether we can encapsulate most of this in a reusable function.
const arr = [1,2,3,4,5,6,7,8,9,10];
const loopFunction = function(arr){
let sum = 0;
for(let i = 0 ; i < arr.length ; i++){
sum = sum + arr[i];
}
return sum;
}
const arraySum = loopFunction(arr)
So next time we need a for loop we can just use our new loopFunction
and pass in the array.
The only problem is that, now, loopFunction can only be used to calculate sums.
What if... we allowed the user to specify what he wants to do with sum
and arr[i]
.
If we go this way, what we are currently calling sum
will not always be a 'sum'. Let's call it acc
(for accumulation)
const arr = [1,2,3,4,5,6,7,8,9,10];
const loopFunction = function(f, myArr){
let acc = 0;
for(let i = 0 ; i < myArr.length ; i++){
acc = f(acc, myArr[i]);
}
return acc;
}
const arraySum = loopFunction((acc, arrElment) => acc + arrElement, arr);
That's it... There is not more to it.
We can get a little bit clever and decide to give our loopFunction
a new name reduce
.
Because it takes an array, and uses the f to essentially 'reduce' the array to one single object.
This one single object gets returned at the end of the operation.
Two things worth noting:
- The body of our "loopFunction" is completely hidden from the outside world.
So different implementations can be used under the hood, the results on the outside should be the same.
A Functional Programming disciple could altogether avoid the internal loop, and use recursive calls - Functional Programming isn't a big fan of loops and re-assignments operations.
- Reduce typically also allows one to specify the initial value of acc and the current index we are at. So the following would be a little bit more correct
const arr = [1,2,3,4,5,6,7,8,9,10];
const reduce = function(f, myArr, initialVal){
let acc = initialVal || 0;
for(let i = 0 ; i < myArr.length ; i++){
acc = f(acc, myArr[i], i, myArr);
}
return acc;
}
const sum = reduce((acc, curr) => acc + curr, arr);
// or
const sum = reduce((acc, curr) => acc + curr, arr, 0);
- I can't speak to whether or not the
reduce
function uses this approach. But this would do the same job.
This has the benefit to provide a mental image and understanding of these functions.
Similarily one could implement filter
const arr = [1,2,3,4,5,6,7,8,9,10];
const filter = function(f, myArr){
let acc = [];
for(let i = 0 ; i < myArr.length ; i++){
if(f(myArr[i], i, myArr)){
acc.push(myArr[i])
}
}
return [...acc];
}
const lessThan5 = filter(curr => curr < 5, arr);
// or
const first5Nums = filter((curr, index) => index < 5, arr, 0);
or, Map
const arr = [1,2,3,4,5,6,7,8,9,10];
const map = function(f, myArr){
let acc = [];
for(let i = 0 ; i < myArr.length ; i++){
acc.push(f(myArr[i], i, myArr))
}
return [...acc];
}
const arrSquares = map(curr => curr * curr, arr);
and so on...