Implement JavaScript Array Methods From Scratch
Aris Zagakos
Posted on December 5, 2021
Table of Contents
Introduction
The JavaScript Array class is a global object that is used in the construction of arrays. Array is a special type of object that is mutable and it is used to store multiple values.
In this article, we will implement our own array methods from scratch. These implementations don't intend to replace the existing methods but to provide a better understanding of how these methods work and their uses.
Methods | Description |
---|---|
indexOf() | Returns the first index at which a given element can be found in the array, otherwise returns -1. |
lastIndexOf() | Returns the last index at which a given element can be found in the array, otherwise returns -1. |
reverse() | Returns the reversed array. |
forEach() | Executes a provided function once for each array element. |
map() | Creates a new array with the results of calling a provided function on every element in the calling array. |
filter() | Creates a new array with all elements that pass the test implemented by the provided function. |
reduce() | Applies a function against an accumulator and each element in the array to reduce it to a single value. |
For a better understanding of Higher Orders Functions and specifically map()
, filter()
and reduce()
methods you can check this article.
Before we start to implement these methods, we will take a quick look on how prototype
and this
work.
What is prototype?
In JavaScript, every function and object has a property named prototype by default. Prototypes are the mechanism by which JavaScript objects inherit methods and properties with each other. Prototypes are very useful when we want to add new properties to an object which will be shared across all the instances.
function User () {
this.name = 'George',
this.age = 23
}
User.prototype.email = 'george@email.com';
User.prototype.userInfo = function () {
console.log('[User name]: ', this.name, ' [User age]: ', this.age);
}
const user = new User();
console.log(user.email); // george@email.com
user.userInfo(); // [User name]: George [User age]: 23
In the example above, we create the function object User
that has the properties name
and age
. Then, we access the User
function object with prototype
property and we add the property email
and the function userInfo()
to it.
What is this?
The value of this
is determined by the object that currently owns the space that this
keyword is in (runtime binding).
function User () {
this.name = 'George',
this.age = 23,
this.printInfo = function() {
console.log(this);
}
this.orders = {
orderId: '12345',
printOrderId: function() {
console.log(this);
}
}
}
const user = new User();
user.printInfo(); // User { name: 'George', age: 23, printInfo: [Function], orders: { orderId: '12345', printOrderId: [Function: printOrderId] } }
user.orders.printOrderId(); // { orderId: '12345', printOrderId: [Function: printOrderId] }
In the example above, we use again the function object User
and add the object orders
to it. The user.printInfo()
prints the this
value and in this case it contains all the properties of the User
function object. The user.orders.printOrderId()
prints only the properties of the orders
object and that happens because the method printOrderId()
is called through the orders
object.
Let's implement the Array Methods
In order to implement the methods, we will access the Array
object via prototype
property and then we will add our new methods. The this
keyword inside the methods has the value of the array that is calling the corresponding array method.
Custom indexOf
Array.prototype.customIndexOf = function (value) {
for (let i = 0; i < this.length; i++) {
if (this[i] == value)
return i;
}
return -1;
}
const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
console.log(output.customIndexOf(2)); // 1
In the example above, the customIndexOf
method takes as a parameter a value and then we iterate the array until we find the corresponding value and return its index.
Custom lastIndexOf
Array.prototype.customLastIndexOf = function (value) {
for (let i = this.length - 1; i >= 0; i--) {
if (this[i] == value)
return i;
}
return -1;
}
const output = [1, 2, 3, 4, 5, 9, 7, 9, 9, 10];
console.log(output.customLastIndexOf(9)); // 8
In the example above, the customLastIndexOf
method takes as a parameter a value and then we iterate the array until we find the last corresponding value and return its index.
Custom reverse
Array.prototype.customReverse = function () {
let left = 0;
let right = this.length - 1;
while(left < right) {
let temp = this[left];
this[left] = this[right];
this[right] = temp;
left++;
right--;
}
return this;
}
const output = [1, 'b', 'abc', { name: 'Jonh' }, 10];
console.log(output.customReverse()); // [10, { name: 'Jonh' }, 'abc', 'b', 1]
In the example above, the customReverse
method reverses in place the array and returns it.
Custom forEach
Array.prototype.customForEach = function (callback) {
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
}
}
const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
output.customForEach(elem => {
console.log(elem);
}); // 1 2 3 4 5 6 7 8 9 10
In the example above, the customForEach
method takes as a parameter a callback function and it is applied on every element in the array. Also, the callback function receives additional the index and the array itself in case that will be used.
Custom map
Array.prototype.customMap = function map(callback) {
const results = [];
for (let i = 0; i < this.length; i++) {
results.push(callback(this[i], i, this));
}
return results;
}
let output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
output = output.customMap(elem => {
return 3*elem;
});
console.log(output); // [ 3, 6, 9, 12, 15, 18, 21, 24, 27, 30]
In the example above, the customMap
method takes as a parameter a callback function and for each element in the array we apply the callback function and we return the result in a new array. Again, the callback function receives additional the index and the array itself in case that will be used.
Custom filter
Array.prototype.customFilter = function (callback) {
const results = [];
for (let i = 0; i < this.length; i++) {
if(callback(this[i], i, this))
results.push(this[i]);
}
return results;
}
let output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
output = output.customFilter((elem) => {
return elem % 2 === 0;
});
console.log(output); // [ 2, 4, 6, 8, 10 ]
In the example above, the customFilter
method takes as a parameter a callback function and for each element in the array we apply the callback function and for the values that pass the callback function we return the result in a new array.
Custom reduce
Array.prototype.customReduce = function (callback, initialValue) {
let value = initialValue;
for (let i = 0; i < this.length; i++) {
value = callback(value, this[i]);
}
return value;
}
const output = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const sum = output.customReduce((acc = 0, elem) => {
return acc + elem;
});
console.log(sum); // 55
In the example above, the customReduce
method takes as parameters a callback function and an accumulator variable and we apply the callback function against the accumulator for each element in the array until to reduce it to a single value.
You can check my github repository here.
Resources
Posted on December 5, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
November 24, 2024
November 22, 2024