How To Implement JavaScript Array Methods From Scratch

PostedDecember 12, 2019 1.8k views JavaScript

While this tutorial has content that we believe is of great benefit to our community, we have not yet tested or edited it to ensure you have an error-free learning experience. It's on our list, and we're working on it! You can help us out by using the "report an issue" button at the bottom of the tutorial.

Introduction

JavaScript includes several functions for working with arrays that go beyond the for loop. You may have used these functions in your own projects and wondered how they work and why you’d use one over another.

There’s no better way to ensure that you understand how something works than building your own version from scratch. In this article you are going to do just that by creating your own versions of map, reduce, sort, and filter from scratch. When you’re done, you’ll have a better understanding of these functions and their uses.

By combining ES6 Arrow Functions with JavaScript Array functions, you can write extremely powerful and clean code.

How JavaScript Array Methods Work

Let’s start with an example. Let’s say you want to iterate through an array of numbers, increment each element by one, and return the new array. In the past, you would need to do several things to accomplish this.

  • initialize a new empty array
  • iterate through each element in th eoriginal array
  • alter that element and put the altered value into the new array

The code would look like this.

const arr =[1,2,3];
const newArray = [];

for (let i = 0; i < arr.length; i++) {
   newArray[i] = arr[i] + 1;
}
return newArray;

With the built in map function, you can accomplish this in a single line of code:

return arr.map( element => ++element);

JavaScript Array methods heavily leverage ES6 Arrow Functions.

Each of the Array functions we will cover accept a function as a parameter. They will iterate through each element of the array and call that function to determine what to do with each element. After iterating through each element and calling the callback function, a new array or item (see reduce below) will be returned.

Implementing Map

map iterates through each element, transforms it in some way, adds it to a new array, and returns the new array.

Let’s start by stubbing out the map function to prove that we can successfully override the original Map function.

Warning: In this article, you’re going to overwrite existing JavaScript functions. This is for educational purposes only; this practice is not recommended in production code.

JavaScript array functions are part of the Array prototype, just like functions on a class in Java, for example. To override them, assign a new function to Array.prototype.

To stub out map, create a new function and assign it to Array.prototype.map:

const myCustomMapFunction = ( callback ) {
    console.log("My Custom Map Function!");
}
Array.prototype.map = myCustomMapFunction;

const arr = [1,2,3];

arr.map();

If you run this code, you see the log message in the console. Now we can add the for loop and print out each element. Since the array itself is what calls the method, we get access to that array by referencing “this”.

const myCustomMapFunction = ( callback ) {
    //this refers to the array
    for (let i = 0; i < this.length; i++) {
        console.log(this[i]);
    }

}

Now, we need to perform whatever transformation is required by calling the callback function. When we do, we will pass a couple of things, the current element and the current index.

const myCustomMapFunction = ( callback ) {
    //this refers to the array
    for (let i = 0; i < this.length; i++) {
        const transformedElement = callback([this[i], i);
    }

}

And lastly, add the transformed elements to a new array and return that array.

const myCustomMapFunction = ( callback ) {
    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        newArray[i] = callback(this[i], i);
    }

return newArray;
}

Implementing Filter

The filter function returns a new array of elements filtered from the original array.

Let’s start by stubbing out our filter method.

const myCustomFilterFunction = () {
    console.log("My Custom Filter Function!");
}
Array.prototype.filter = myCustomMapFunction;

Now, let’s set up the for loop to iterate through each element.

const myCustomFilterFunction = ( callback ) {
    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        console.log(this[i]);
    }

}

Inside of the for loop, we need to decide whether or not to add each element to the new array. This is the purpose of the callback function, so we use it to conditionally add each element. If the return value is false, push the element on to the return array.

const myCustomFilterFunction = ( callback ) {
    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        if(callback(this[i])){
            newArray.push(this[i]);
        }

    }

    return newArray;

}

Implementing Sort

The sort function returns a sorted array from the original array.

Let’s start again by stubbing out out sort function with a for loop.

const myCustomSortFunction = ( callback ) {
    const newArray = [];

    for (let i = 0; i < this.length; i++) {
        console.log(this[i]);
    }
}
Array.prototype.filter = myCustomMapFunction;

We are going to be using Bubble Sort for this sort implementation. Here’s the strategy:

  • repeatedly iterate through items in array
  • compare adjacent itmes and swap if they are not in order
  • after iterating through n^2 times, the array is sorted

With Bubble Sort, you have to iterate through the array fully once for each element in the array. This calls for a nested for loop where the inner loop iterates through stopping one short of the final element, so we can add that now.

Note: This isn’t the most efficient method for sorting. It’s just for educational purposes.

const myCustomSortFunction = ( callback ) {
    const newArray = [];

    for (let i = 0; i < newArray.length; i++) {
        for(let j =0; j < newArray.length -1; j++){ 
        }
    }
}

We also don’t want to alter the original array. To avoid this, we can copy the original array into the new array using the Spread Operator.

const myCustomSortFunction = (callback) {
    const newArray = [...this];

    for (let i = 0; i < newArray.length; i++) {
        for (let j = 0; j < newArray.length -1; j++) { 
        }
    }
}

The callback function takes two parameters, the current element and the next element, and will return whether or not they are in order. In our case, if the callback function returns a number greater than zero, we want to swap the two elements.

const myCustomSortFunction = (callback) {
    const newArray = [...this];

    for (let i = 0; i < newArray.length; i++) {
        for (let j = 0; j < newArray.length -1; j++) {             
            if (callback(newArray[j], newArray[j+1]) > 0) {
                // swap the elements
            }
        }
    }
}

To swap the elements, we make a copy of one, replace the first one, then replace the second one with the copy. When we are finished, we return the newly sorted array.

Array.prototype.sort = function(callback) {
  const newArray = [...this]; 

  for (let i =0; i < newArray.length; i++){
    for (let j =0; j < newArray.length -1; j++){         
        if (callback(newArray[j], newArray[j+1]) > 0 ){
            const temp = retVal[j+1];
            retVal[j+1] = retVal[j];
            retVal[j] = temp;
        }
    } 
  }
  //array is sorted
  return newArray;
}

Implementing Reduce

The reduce function iterates through each element and returns one single value

reduce does not return a new array like the other functions. It actually “reduces” the elements in an array to one final value, a number, a string, or an object. One of the most common reasons for using reduce is when you want to sum up all the elements in an array of numbers. Let’s start with stubbing the function out like we did previously.

const myCustomReduceFunction = ( callback ) {
    console.log("My Custom Reduce Function!");
}
Array.prototype.reduce = myCustomReduceFunction;

const arr = [1,2,3];

arr.reduce();

For reduce to return one final value, it needs a starting value to work with. The callback function that the user passes along will determine how to update this “accumulator” based on each element of the array and return it at the end. The callback function must return the updated accumulator.

We can add our for loop now, and make a call to the callback function. The return value becomes the new accumulator. After the loop ends, we return the accumulator.

const myCustomReduceFunction = ( callback, accumulator ) {

    for (let i = 0; i < this.length; i++) {
        accumulator = callback(accumulator, this[i], index);
    }
    return accumulator;
}

Conclusion

JavaScript’s array functions are extremely useful. By reimplementing them yourself, you have a better understanding of how they work so you can use them effectively.

0 Comments

Creative Commons License