Skip to content

Instantly share code, notes, and snippets.

@KMurphs
Last active January 20, 2021 16:34
Show Gist options
  • Save KMurphs/575c8e8342642e5d35a793f6db606d8c to your computer and use it in GitHub Desktop.
Save KMurphs/575c8e8342642e5d35a793f6db606d8c to your computer and use it in GitHub Desktop.
Reduce under the hood

The Imperative Way

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.



The Semi-Reusable Loop

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)



The Fully-Reusable Loop aka reduce

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.



Concluding Notes

Two things worth noting:


  1. 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.



  1. 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);



  1. 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...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment