Understanding array methods in javascript by building them from scratch
16 May 2021 / 6 min readJavascript arrays are packed with some very handy built-in methods. These methods can seem confusing when you're first starting out with javascript.Some of them of can be confusing even after years of coding. One of the best ways to gain a deeper understanding of these methods is to actually build them from scratch.
That's exactly what we're going to cover today. We're going to cover the most popular Array methods in javascript.
All methods that we're going to cover in here are inherited from Array.prototype
. Although it's possible, it is not recommended to override the default methods but instead to add your own like Array.prototype[myMethod]
Foreach
Array.prototype.forEach
is a method that iterates through
an array and for each item it applies a callback function.
This method doesn't return anything and is generally used as an elegant alternative to the classic for loop, providing a simpler and cleaner way of iterating through an array.
Example
const data = [1, 2, 3, 4, 5]
data.forEach((value, index) => {
console.log('current value: ' + value)
console.log('current index: ' + index)
})
The callback function accepts 3 arguments which are the current current value, current index and the initial array. Note the arguments are optional, that's why we didn't pass a third argument which is usually not used at all.
Now let's go ahead and implement our own forEach!
Array.prototype.myForEach = function (callbackFn) {
for (let i = 0; i < this.length; i++) {
callbackFn(this[i], i, this)
}
}
Note that this
is actually the input array.
So what we're doing is just iterating through the input array and applying the callback function on each iteration.
If you go ahead and replace forEach
with myForEach
in the example above
you should get the same result.
Map
Array.prototype.map
is a method that iterates through an
array and for each item it applies a callback function. The only difference between forEach
and map
is that the latter returns a new array,
equal in length as the initial one. The elements of the new array will be the elements
returned by the callback function on each iteration
Example
const data = [1, 2, 3, 4, 5]
const newData = data.map((value, index) => {
return value + index
})
console.log(newData) // [ 1, 3, 5, 7, 9 ]
Let's see how we can implement our own method.
Array.prototype.myMap = function (callbackFn) {
const newArray = []
for (let i = 0; i < this.length; i++) {
newArray.push(callbackFn(this[i], i, this))
}
return newArray
}
As you can see, it looks very similar to the implementation of the forEach
method, except that now we also want to store
each element in a new array after we apply the callback function. And after we've iterated through each element we just need to return the
new array
If you go ahead and replace map
with myMap
in the example above
you should get the same result.
Filter
Array.prototype.filter
is a method that iterates through an array and for each item it applies a callback function. The method returns a new array containing elements from the initial array for which the callback function returned true
Example
const data = [1, 2, 3, 4, 5] \n
const newData = data.filter((value, index) => {
return value % 2 === 0
}) \n
console.log(newData) // [2, 4]
Let's see how we can implement our own filter!
Array.prototype.myFilter = function (callbackFn) {
const newArray = []
for (let i = 0; i < this.length; i++) {
const isTrue = callbackFn(this[i], i, this)
if (isTrue) {
newArray.push(this[i])
}
}
return newArray
}
We initialize an empty array, just as we did on the map but the difference now is that we don't want to push every single element.
We want just the ones that evaluate to true
after calling the callback on them
If you go ahead and replace filter
with myFilter
in the example above
you should get the same result.
Reduce
Unlike the methods that we've previously explored, Array.prototype.reduce
method also takes in a second argument that is the initialValue (also called accumulator). On each iteration, the reduce function is going to re-assign the accumulator with the result of calling the callback function.
Example
const data = [1, 2, 3, 4, 5]
const reduced = data.reduce((acc, curr) => acc + curr, 0)
console.log(newData) // 15
Let's see how we can implement our own reduce!
Array.prototype.myReduce = function (callbackFn, accumulator) {
for (let i = 0; i < this.length; i++) {
accumulator = callbackFn(initialValue, this[i], i, this)
}
return accumulator
}
So all we're doing is just calling the callback function with the accumulator and all the other arguments that we've seen in the previous methods.
If you go ahead and replace reduce
with myReduce
in the example above
you should get the same result.
Concat
Array.prototype.concat
is method that combines two or more arrays together. The arrays are combined into a new one without modifying any of the input arrays.
Example
const data1 = [1, 2, 3]
const data2 = [4, 5, 6]
const combined = data1.concat(data2)
console.log(combined) // [1, 2, 3, 4, 5, 6]
Note that more than one 1 array can be passed as arguments. The method will just combine them together in the same order they were passed in.
Let's see how we can build our on concat!
Array.prototype.myConcat = function () {
const newArray = [...this] \n
for (let i = 0; i < arguments.length; i++) {
if (Array.isArray(arguments[i])) {
for (let j = 0; j < arguments[i].length; j++) {
newArray.push(arguments[i][j])
}
} else {
newArray.push(arguments[i])
}
}
return newArray
}
A couple of things happen in here. The first thing you might notice is that
we didn't specify any parameters. That is because we should be able to pass
as many parameters as we wish. The way we can access all arguments that were
passed when calling the function, is to use the arguments
keyword.
Another weird thing that you might've noticed is the [...this]
on line 2. That code just copies the original array into a new one so any changes made won't affect the initial array. This is the equivalent of iterating thorough the initial array and pushing each element into the newArray
.
Ok, so we iterate through all the arguments, and for each one of them we want to check if we're dealing with an array. If the user didn't pass an array then we can just push the value into the final array. But if the user did pass an array than we want to iterate through that array and push each element into the final array.
If you go ahead and replace concat
with myConcat
in the example above
you should get the same result.
Join
Array.prototype.join
is method combines all array elements into a new string, separated by a string provided as the first argument. The initial array is not modified.
Example
const data = [1, 2, 3, 4, 5]
const result = data.join('//')
console.log(result) // 1//2//3//4//5
Let's see how we can build our own join method!
Array.prototype.myJoin = function (separator) {
let resultString = ''"
for (let i = 0; i < this.length - 1; i++) {
resultString += this[i]
resultString += separator
}
resultString += this[this.length - 1]
return resultString
}
So all we're doing is to iterate through the input array and add each
element followed by the separator to the final result string. Notice that
we're going just until this[this.length - 1]
index. That is
because we don't want to include the separator after the last element.