Static, Instance, and Looping methods in JS Arrays

manthony
The Startup
Published in
7 min readNov 3, 2020
Static, instance, and looping methods of arrays!

In the last article I had written on arrays, we had gone over the very basics of what an array is and some useful methods associated with them. We were able to create an array, add and remove items from it, and get its length. In this post, we will dive a bit deeper- looking at some powerful array methods to really demonstrate the value of an array as a data structure, as well as break up the different methods available to us into three distinct categories.

The three methods associated with arrays

There are generally three different methods- static methods, instance methods, and looping methods. Looping methods are, in fact, instance methods themselves, but I believe they warrant their own category due to their unique and powerful ability. So what is the difference between them?

Static methods

Static methods can only be called with the prefix Array. They relate directly to an Array as a whole, rather than a specific array that you have created or are working with. Examples of some static methods are below:

Array.from()
// will take another JS iterable and turn it into a proper array
Array.isArray()
// Will return a bool based on if the passed argument is an array or not
Array.of()
// A longhanded version of using an array literal [] to declare a new array

These static methods, while useful (namely isArray() for testing and from() for converting one of the many other JS iterables to a proper array to take advantage of other instance methods) are not the real star of the show here. Let us look ahead to some other useful instance methods and explore the differences.

Instance Methods

Instance methods (or prototypal methods) are quite the opposite of static methods- you cannot call any of them with the Array prefix, rather only an instance that you have declared. Let’s look at our first instance method, fill()to demonstrate this.

const demoList = [1, 2, 3]
// declare a new instance of array with array literal with three items
demoList.fill(0, 0, 3)
// fill with 0 from index 0 to index 3
console.log(demoList)
// [0, 0, 0]
Array.fill(0, 0, 3)
// Array.fill is not a function

We see that we cannot call fill() on Array, but only on an instance of Array that we define.

Some other very useful instance methods that I find myself using often are indexOf(), reverse(), slice(), join()and includes(). We will briefly go over examples of each of these.

.indexOf()

indexOf() will return the index of the first value in your array that matches the argument you pass it. I should note the inverse of this is lastIndexOf(), which will return the index of the last matching value.

const demoList = ['cat', 'mouse', 'cat']demoList.indexOf('cat')
// 0
demoList.lastIndexOf('cat')
// 2

.reverse()

reverse() will reverse an array for you without having to construct your own function to do so. It should be noted that the native reverse method is not the most performant, but does run at linear time and therefore is good enough for most cases!

const demoList = ['here', 'we', 'go']demoList.reverse()
// ['go', 'we', 'here']

.slice()

slice() can return just the part of an array that you would like without mutating the origin array. I most recently utilized this method while making fetch requests to a 3rd party API. I only wanted to request 50 items at a time, so each call would slicethe existing array of 500 items to keep response times fast and the data flowing on the page.

const demoList = ['lots', 'of', 'items', 'but', 'i', 'am', 'not', 'sure', 'i', 'will', 'need', 'them', 'all']demoList.slice(0, 3) // give me items from the 0th index to the 3rd
// ['lots', 'of', 'items']
console.log(demoList)
// ['lots', 'of', 'items', 'but', 'i', 'am', 'not', 'sure', 'i', 'will', 'need', 'them', 'all']

.join()

join() is often used to join items from an array back into a string. It is often used in conjunction with the string instance method, split()to first split a string into individual array items, perform a mutation or filter them, and then finally join() them back together. It takes a single argument, which is what you would like to join each of these items on.

const demoList = ['lets', 'make', 'this', 'a', 'statement']demoList.join(' ')
// 'lets make this a statement'
demoList.join('')
// 'letsmakethisastatement'
demoList.join(',')
// 'lets,make,this,a,statement'

.includes()

includes() also returns a boolean value based on whether or not your argument exists within the target array. This method can be very valuable to conditionally check if a value exists, especially in an unsorted array where it is necessary to check each value. It must be a full match to an item within the array.

const demoList = ['needle', 'in', 'a', 'haystack']demoList.includes('needle') 
// true
demoList.includes('dog')
// false

Looping Methods

Ah, here at last. Looping methods are something I am reaching for all the time, especially in react projects where I am often wanting to iterate over a list of items and do something with them- whether that displays a component or update a value by reducing them. Some of these examples may be contrived, but it is good to see them in a simple and easy to follow way first!

Here we will take a look a map(), filter(), and reduce()

.map()

map() is a workhorse, iterating over each and every item in your array, running it through a function, and returning its new value to a new array. map() will always return an array, and will always return an array the same length as the one you applied it to. Let’s look at an example of it at work

const demoList = [1, 2, 3, 4]demoList.map(number => number * 2)
// [2, 4, 6, 12]

In this example, we created an inline function with ES6 implicitly returned fat arrownumber => number * 2 that multiplied each number by 2, and returned the resulting value into a new array. demoList remains unchanged!

You can also access the index by exposing the second argument of map, often represented as i , and add conditions to each item.

demoList.map((number, i) => {
if (i > 1) {
return number * 2
}
return number
})
// [ 1, 2, 6, 8 ]

It is especially great for creating a list from a bloated array of objects where you would just like maybe one of the property's values

const listOfObjects = [{key: 'value', key2: 'value2'}, {key: 'value', key2: 'value2'}, {key: 'value', key2: 'value2'}]listOfObjects.map(obj => obj.key)
// [value, value, value]

Or maybe to strip an unwanted attribute completely, we can use some handy ES6 destructuring to keep only what we want!

listOfObjects.map(({key, ...key2}) => key2)
// [ { key2: 'value2' }, { key2: 'value2' }, { key2: 'value2' } ]

.filter()

filter() can iterate over each item in the array and return only what you are looking for. It is useful when you have a large list of items and you are only targeting certain values, types, or perhaps objects with certain attributes.

const demoList = ['string', 'string', 8]demoList.filter(item => typeof item === 'string')
// [ 'string', 'string' ]

We iterate over each item and check its type- in this case, it only will be in the resulting string if the type matches ‘string’. Note that the resulting array has a length of 2 while the original array had a length of 3- unlike map, filter will always return an array but the length can vary based on conditions.

const demoList = [4, 3, 5, 10, 7, 5, 11, 19, 2]demoList.filter(number => number < 10)
// [ 4, 3, 5, 7, 5, 2 ]

Here we only want to return values that are under 10.

const demoList = [{user: 'manthony', isCool: false}, {user: 'katie', isCool: true}]demoList.filter(user => user.cool)
// [ { user: 'katie', isCool: true } ]

Didn’t make the cut there, but my wife, Katie, did. Typical.

reduce()

reduce() most often will condense values, flatten arrays, find averages, or tally occurrences. It can be a difficult method to grasp. The key here is to remember there is a running total, known as the accumulator, and the current item in the list you are performing on, currentValue. There are also index and array arguments available, but for the scope of this example, they won’t be necessary.

First, let’s reduce an array of values into a sum of all the values.

const demoList = [1, 2, 3, 4, 5]demoList.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
// 15

Next, let’s tally all occurrences of characters in a string, using a mixture of some of the other instance methods from above.

const demoString = 'how many times was each letter in this string?'const splitString = demoString.split('')

splitString.reduce((accumulator, currentValue) => {
if (currentValue in accumulator) {
accumulator[currentValue] += 1
}
else {
accumulator[currentValue] = 1
}
return accumulator
}, {});
// { h: 3,
o: 1,
w: 2,
' ': 8,
m: 2,
a: 3,
n: 3,
y: 1,
t: 5,
i: 4,
e: 4,
s: 4,
c: 1,
l: 1,
r: 2,
g: 1,
'?': 1 }

Of course, accumulator and currentValue can be named whatever you would like- they are merely placeholders- and in this case, it might make sense to do that to add clarity.

splitString.reduce((allLetters, currentLetter) => {
if (currentLetter in allLetters) {
allLetters[currentLetter] += 1
}
else {
allLetters[currentLetter] = 1
}
return accumulator
}, {});
// { h: 3,
o: 1,
w: 2,
' ': 8,
m: 2,
a: 3,
n: 3,
y: 1,
t: 5,
i: 4,
e: 4,
s: 4,
c: 1,
l: 1,
r: 2,
g: 1,
'?': 1 }

Much better :)

I hope that this helped you understand the differences between static, instance, and looping methods. Next time, I will demonstrate real uses of these functions to help solidify their usage and value.

--

--