Table of Contents
- Introduction
- What are arrays?
- How do we create arrays?
-
Methods
- Basic Methods: pop, push, shift, unshift, splice, slice
- Advanced Methods: reduce, sort, concat, filter, join, map, find, forEach
- Fun Methods: toString, includes, fill, indexOf, findIndex
- Conclusion
- Resources
Introduction
One data structure that many other complex ones build off of is the humble array. Therefore, it’s important to have a strong foundational understanding and knowledge of arrays before diving into other data structures. In this post we will cover what arrays are, how to create them, and 20 methods ranging from ones you will commonly use, some that are a bit more complex (mainly because of callback functions), and some fun ones to know as well.
Before you go, if you’re interested in learning more data structures and want another resource for algorithms, check out the series Megan Lo and I are collaborating on! The series will focus on data structures and algorithms, and our first post covers the crossover of Strings and Arrays. If you need a refresher on Strings, check out her post here, otherwise visit our collab and stay tuned for more!
Without further ado, let’s dive into the wonderful world of arrays!
P.S. Since this is a very long post, feel free to skip around as you like 😉
What are arrays?
According to MDN, JavaScript arrays are “list-like objects whose prototype has methods to perform traversal and mutation operations”. In other words, arrays organize their items sequentially and have built-in methods that allow you to easily lookup and add/remove information based on its position. Array positions, also known as indexes, start at zero.
Fun fact: Unlike non-scripting languages like Java, C, or C++, JavaScript (as a scripted language) does not have static arrays where you need to specify in advance how many elements you plan to store because they have a fixed size. Instead, JavaScript arrays are dynamic, meaning the size of it will grow or shrink as needed.
Fun Fact: Arrays are a special type of object! That said, the array’s object properties are kept separate from its elements and the methods you would use on the array’s elements cannot be used on its object properties. To set or access the array’s object property collection, you need to use bracket or dot notation.
console.log(typeof new Array()) // “object”
console.log(typeof [1, 2, 3]) // “object”
Things to keep in mind:
- Arrays have fast lookups of O(1) time because you can simply retrieve an element based on its given index, no matter how large the size of the array is.
- It is time expensive O(n) to insert or delete from an array because it requires the other elements to “scoot” over to make room or fill the gap.
How do we create arrays?
Essentially there are two ways to create an array:
- With the array literal
let literalEmptyArray = []
let literalFilledArray = [1, 2, 3]
- With the new constructor
let constructorEmptyArray = new Array()
let constructorFilledArray = new Array(1, 2, 3)
That said, a third way to create an array is the of
method! Essentially the of
method creates a new Array instance from the passed in number of arguments, regardless of number or type of the arguments. The difference between the of
method and Array
constructor is what they do with the arguments; Array.of(7)
creates an array with a single element, 7, whereas Array(7)
creates an empty array with a length property of 7 (Note: this implies an array of 7 empty slots, not slots with actual undefined values)
Array.of(1); // [1]
Array.of(1, 2, 3); // [1, 2, 3]
Array.of(undefined); // [undefined]
Methods
Before going into some of the numerous (I roughly estimate more than 35) methods that arrays have, let’s first review what it means for something to be destructive vs non-destructive.
- Destructive: The action mutates the original array, meaning that once you perform the action on the original array, you will not be able to have the original’s information again. Rather, the original has become updated.
- Non-destructive: The action does not mutate the original array, meaning that once you perform the action on the original array, you will have the original’s information. Therefore, you will be able to have both the original and updated information.
Understanding and being aware of when a method is destructive vs non-destructive is important when you will ultimately need to decide which method to use. Now, let’s take a look at some basic, advanced, and fun methods!
Basic Methods
The methods we will be covering are: pop
, push
, shift
, unshift
, splice
, and slice
. To demonstrate each method, the base array we will be referring to is:
let iceCream = [“vanilla”, “chocolate”, “strawberry”, “green tea”]
The 4 most common use cases for methods on an array is to destructively add or remove an element from the beginning or end of it.
In case you can't see the image below, here's a summary of the methods:
-
push
: Add an item to the end of an Array -
pop
: Remove an item from the end of an Array -
unshift
: Add an item to the beginning of an Array -
shift
: Remove an item from the beginning of an Array
Other common cases are copying or removing a section of the array. While they have similar names, these methods are splice
and slice
and it's important to remember whether or not you want the action to be destructive or non-destructive.
splice
: Remove an item by index position (destructive)
When using splice
, you must pass in what index you want to start removing items from (inclusive). You can optionally include a second argument index to say where you want to stop removing items from (inclusive), however if you don’t the method will automatically remove to the end. Additionally, starting from the third argument, any that you include will be elements added to the array, beginning from start (first argument). If you do not specify any elements, splice
will only remove elements from the array. That said, if no arguments at all are passed in, the return value will be an empty array.
// general
Array.splice(startIndex)
// including the optional parameters
Array.splice(startIndex, endIndex, newElement)
Don't forget the original array for the following example!
slice
: Copy an Array (non-destructive)
If you simply want to make a copy of an array you do not need to pass in any arguments. That said, you do have the option to include the starting index (inclusive) and ending index (non-inclusive) to copy from. This method is often used over splice
because it avoids the “side-effect” of mutating the original array.
If you do not pass in any arguments, by default the entire original array will be copied. If either index is negative, it extracts starting from the end or last element (Array.length
- index). On the other hand, if the arguments you pass in are larger than the actual array (for example, an array with 5 elements but passing in arguments to start at 10 and end at 50) the return value will be an empty array.
// general
Array.slice()
// including the optional parameters
Array.slice(startIndex, endIndex)
Don't forget the original array for the following example!
Advanced Methods
The methods we will be covering in this section are: reduce
, sort
, concat
, flat
, filter
, join
, map
, find
, and forEach
. Before continuing on, it’s important to know that many of the methods have the same parameters; in this case filter
, map
, find
, and forEach
. Instead of repeating it each time, I’ll leave the explanation of the parameters up here for you to refer to!
There are three arguments you can pass into the callback function, two of them being optional. The one argument you must pass in is the current value, which represents the current element being processed. The other two arguments are the index of the current element and array the method was called upon. Besides the callback function, you can also use the thisArg
parameter, which is the value to use as this
when executing the callback. However, if the callback uses an arrow function the thisArg
can be omitted because all arrow functions lexically bind the this
value.
reduce
: Reduce to a single value (destructive)
Essentially, the reduce method takes in a callback function that executes the callback on each element of the array, resulting in single output value. The callback (reducer) function you provide must at the very least include two parameters: the accumulator and current value. The accumulator accumulates callback's return values; in other words, it is the accumulated value previously returned in the last invocation of the callback On the hand, the current value is the value currently being processed in the array.
Optionally, the reduce method can take in a second argument that will represent the initialValue. This value is what the accumulator will start as if it is passed in. Additionally, the callback can take in other parameters for the index and array, which represent the index of the current element being processed and the array the reduce method was called on.
Array.reduce((accumulator, currentValue, index, array) => {
// do something
return accumulator + currentValue
})
sort
: Sorts the elements (destructive)
When calling this method on the array it will sort it in place and return the sorted version. By default the elements will be sorted in ascending order by converting the elements to strings and then comparing their Unicode code points. It’s important to know how they are sorted because in a numeric sort, 9 comes before 80, but because numbers are converted to strings, "80" comes before "9" in the Unicode order. Something important to note, all undefined elements are sorted to the end of the array.
Optionally, and if you want to be more specific on how to sort (i.e. for integers), you can pass in a callback (compare) function that compares two arguments; the first and second element, often referred to as a and b, to each other. Under the hood, if the callback function returns:
- less than 0, it means that the current order is correct; the first element will remain before the second element (a will still come before b)
- 0, it means that the elements are equal to each other; the order will remain the same with respect to each other, but sorted with respect to all different elements.
- greater than 0, it means that the current order is incorrect; the second element will be before the first element (b before a)
Array.sort()
// including the optional parameters
Array.sort((a, b) => a - b)
concat
: Merge arrays (non-destructive)
This method is used to merge two or more arrays, returning a new array without mutating the originals. To be more specific, the new array has all of the elements of the array it is called on, followed in order by, for each argument, the elements of the argument or the argument itself. However, if an argument is a nested array it will not take the nested array out, rather it will only remove it from the array it is in (one-level deep)
Fun Fact: concat
copies the object references of the original into the new array so that both the original and new array refer to the same object! Therefore, if a referenced object is modified, the changes are visible to both the new and original arrays.
Array1.concat(Array2)
flat
: Creates a new array with the sub-array elements concatenated into it (non-destructive)
Building off of the concat
method, the flat
method creates a new array with all sub-array elements concatenated into it recursively up to the specified depth. For a single level array, this accomplishes that same thing as the reduce
method calling upon the concat
method on its accumulator. On the other hand, to enable deep level flatten without the flat method, you can use recursion with reduce and concat.
While not required, you can optionally pass in an argument that specifies how deep a nested array structure should be flattened. By default this argument is 1, for a single level array.
Array.flat()
// including the optional parameters
Array.flat(2)
filter
: Returns all the elements that pass the test function provided (non-destructive)
This method creates a new array with all the elements that pass a callback (test) function. As it tests each element of the array it is called on, it returns a value that coerces to true to keep the element, or to false otherwise. When the value is false, it essentially skips over the element and does not include it in the array. If nothing passes the test, an empty array is returned. For more information on the parameters for this function, skip back to the beginning of the Advanced Methods section!
Array.filter(element => {
// do something
element > 6
})
join
: Join all the elements from an array to a string (non-destructive)
join
creates and returns a string by concatenating, or joining, all of the elements of the array it was called on. By default the elements are separated by commas, however you can specify what you want to join/separate the elements by. On the other hand, if there is only one element in the array, the single item will be returned as a string without separators, and if there are no elements, an empty string is returned.
As mentioned, including an argument for the separator parameter is optional if you want the elements to be joined with a comma. Passing in an empty string as the argument will result in the elements joined without any characters/separators. Otherwise, the parameter is what you want to separate each pair of adjacent elements of the array for the returned string. If necessary, the separator is converted to a string.
Array.join()
map
: Creates a new array with the results of a callback function (non-destructive)
map
takes in a callback function that is called once for every element of the array it is called on. Each time the callback is executed, it returns the value into the new array, which is returned at the end. That said, if you do not use the returned (new) array and/or do not return a value from the callback, using the map
method is considered anti-pattern. Instead you should use the forEach
method or a for loop. For more information on the parameters for this function, skip back to the beginning of the Advanced Methods section!
Array.filter(element => {
// do something
element * 2
})
find
: Return the value of the first element that satisfies the provided function (non-destructive)
The find
method only returns the first value of the element that satisfies the callback (test) function. If no element passes the test, the find
method will return undefined. That said, if you want to return the index of the element instead of its value, you can use the findIndex
method instead. For more information on the parameters for this function, skip back to the beginning of the Advanced Methods section!
Array.find(element => {
// do something
element > 6
})
forEach
: Loop over an Array (non-destructive)
Similar to a for
loop, forEach
executes a callback function once for each element in the array. While the forEach
method will not mutate the array it was called on, it is possible for the callback function to mutate it. That said, the forEach
method expects a synchronous function, always returns undefined, and is not chainable. Therefore, the typical use case is to execute side effects at the end of a chain. For more information on the parameters for this function, skip back to the beginning of the Advanced Methods section!
Array.forEach(element => console.log(element))
Fun Methods
Now, time for some “fun” methods! The methods we will cover here are: toString
, includes
, fill
, indexOf
, and findIndex
.
toString
: Returns a string representing the array and its elements (non-destructive)
Like its name, the toString
method turns the array’s elements it was called on to a string. To be more specific, this method joins the array and returns one string containing each array element separated by commas.
Fun fact: JavaScript calls the toString
method automatically when an array is to be represented as a text value or when an array is referred to in a string concatenation.
Array.toString()
includes
: Returns a boolean if a value exists in an array (non-destructive)
includes
determines whether an array includes a certain value among its entries, returning true or false as appropriate. It does this by checking each element for equality with the value rather than using a testing callback function. That said, if you need to find if any element satisfies a provided testing callback function, you can use the some method.
The argument you must pass in is the value you want the method to search for; keep in mind when comparing strings and characters, includes
is case-sensitive. The optional second argument is the index to start searching for the value and by default is zero. That said, if the index passed in is greater than or equal to the length of the array, false is returned and the array will not be searched. On the other hand, if the index is negative the method uses the absolute value of it as the number of elements from the end of the array at which to start the search
Array.includes(searchValue)
fill
: Fills all the elements of an array with a static value (destructive)
The fill
method changes all elements in an array to a static value, from a start index to an end index. It then returns the modified array with the filled values.
There are three parameters however only the first one is required. The first argument you must pass in is the value to fill the array with. It’s important to know that all of the elements in the array will be this exact value. The other two optional parameters are for the start index, default is zero, and the end index, default is array.length.
Array.fill(staticValue)
indexOf & findIndex: Find the index of an item in the Array (non-destructive)
Similar to each other in that both return the first index that satisfies a condition. However, while findIndex
is based on the element that satisfies a testing callback function, indexOf
checks each element for equality with the value. Additionally, -1 is returned by indexOf
if the element you are searching for is not present, whereas -1 is returned by findIndex
if nothing satisfies the callback. That said, if you need to find if any element satisfies the provided testing function, you can use the some method.
findIndex
has the same parameters detailed in the beginning of the Advanced Methods section. On the other hand, indexOf
takes in an argument for the element to search for and optionally the index to start searching for. If you include the second argument of the index to start searching and the index is greater than or equal to the array's length, -1 is returned, which means the array will not be searched.
Note:
Array.indexOf(searchValue)
Array.indexOf(searchValue, startIndex)
Array.findIndex(element => {
// do something
element === "cat"
})
Conclusion
Congratulations! I declare you “master of JavaScript arrays and (most) array methods”!
But seriously though, this was a lot of information and I hope you’ll be able to refer to it in the future! Here are some key takeaways, as well as a reminder of what methods we covered:
- Arrays organize their items sequentially and have built-in methods that allow you to easily lookup and add/remove information based on its position.
- JavaScript arrays are special type of object and unlike non-scripting languages, are dynamic
- To create arrays you can use the array literal, new constructor, or of method
- Additionally, you can copy, concatenate arrays, and convert a string into an array by using the spread operator
-
Basic methods about adding, removing, or copying an array:
pop
,push
,shift
,unshift
,splice
,slice
-
Advanced methods to merge:
reduce
,concat
,flat
,join
-
Advanced methods do something based on a callback:
sort
,filter
,map
,find
,forEach
-
Fun methods that have to do with the value or index:
includes
,indexOf
,findIndex
-
Fun methods to know:
toString
,fill
If you made it to the end thank you so much for reading, I hope you found it useful! I recommend checking out my friend Megan's When to Use these String Methods in JavaScript, for a similar post on String methods. And don't forget about my collaboration post with Megan that covers the crossovers between Strings and Arrays!
Top comments (2)
Great informative post! But if I may give you a piece of feedback. For your next technical posts like these, can you split them into two or three posts? That would make them easier to follow and to grasp.
Looking forward to your series with @mehmehmehlol 👊
Thank you for the feedback, definitely noted!