A Guide to the Array Data Structure in JavaScript

Everything about the array data structure in JavaScript.

JavaScript provides you with 2 ways to work with indexed list data. Both are powerful and address a very specific usage case. Understanding the difference and how arrays work may help you in the decision of whether to use an array or not for your program data manipulation.

Video Version of This Article

This post is a more detailed article version of the Array Data Structure Series on Youtube that you can check if you prefer videos.

Watch Videos

JavaScript Array vs Typed Array

JavaScript array is a list-like object which means that it does not follow the traditional array definition or implementation. It is more like an indexed-associative array that provides a mechanism to read and write binary data in memory. It grows dynamically and can contain any mix of JavaScript data types. It is pretty much a list.

On the other hand, typed arrays came up from this need to read binary data quickly since JavaScript arrays are slow and often need to be optimized by engines so they perform faster read and write operations. Typed arrays are not to be confused with an array that is of a certain type(string, boolean, etc). It contains Buffer binary data which are pretty much represented with numbers.

Typed arrays have 2 parts, buffers and views. The buffer — implemented by ArrayBuffer — represents the chunk of data(the actual memory) and provides no mechanism to access this data. The view provides the context (data type, number of elements, starting offset, etc) and the mechanism to access the data(memory). The view is what turns the data into a typed array and it is implemented using Data View.

Typed arrays is not an Array, therefore, using the Array static method “isArray” to check it will always return false. However, there is nothing wrong with using TypedArray instead of Array to work with numbers, especially if you want to take advantage of its speed and low memory footprint.

When to use Array vs Typed Arrays?

Typed arrays are for when you are manipulating or working with binary data. It is the best and the fastest way to access data in memory. An example of this would be when you are manipulating audio, video, or image bitmap — binary transferring through HTTP/WebSocket/WebRTC or between workers — reading or writing files to the filesystem, etc.

The array is a general-purpose list and it is recommended to be used for anything else.

They both share a good amount of the prototype methods and properties so a lot of what you will read here will apply to both. For example, typed arrays do not come with push and pop methods so it will be good to know how to change typed arrays into an array to use those missing methods.

The main focus of this article is the actual array and not the typed array object.

Creating and Filling an Array

You can use the Array object or the square bracket notation to initialize your array. The best and shorter way is the square bracket option but the Array object can be super handy in some situations. You can also use the static “of” method in the Array object but it is rarely the case where this is more convenient.

const numbers1 = [5, 7, 2, 5, 2, 9];
const numbers2 = new Array(5, 7, 2, 5, 2, 9);
const numbers3 = Array.of(5, 7, 2, 5, 2, 9);

Note: Omitting the “new” in front of Array will still give you the array but it is recommended to always use the “new” keyword when initializing constructors.

The above examples would result in the same thing but the first option is the best. The Array static “of” method is second best because it is more functional and it is easier for compilers since they don’t have to deal with the “new” keyword or instances.

The below options create an array of 9 empty slots but the first one can be hard to read. If you only specify a number for the Array object, it is taken as the length of the array. If you specify more than one or a single non-number argument, they all will be considered to be the items of the array you are creating.

[,,,,,,,,,]; // [ <9 empty items> ]
new Array(9); // [ <9 empty items> ]

Note: Empty slots are ignored when you are iterating the array. If you try to print the above array examples as you iterate them, nothing will show in the console.

If you want a fixed size array you can take advantage of the fact that arrays are objects and seal the array.

const months = Object.seal((new Array(12)).fill(undefined));months[0] = 'January';
months[11] = 'December';
months[12] = 'Over';
console.log(months);
/* prints
[
'January', undefined,
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined, '
December'
]
*/

The above example creates an array of 12 empty slots but fills them with “undefined” because if you try to seal an empty array it will ignore the empty slots and treat them as a zero-length array which will prevent you from updating the items later.

The fill” method takes the item to fill the array with and the optional start and end index where to fill the array with the item. The below example will fill the numbers array with the number “3” from index 1(inclusive) to index 9 (exclusive).

const numbers = new Array(10)
numbers.fill(3, 1, 9)
console.log(numbers)
/* prints
[ <1 empty item>,
3, 3, 3, 3, 3, 3, 3, 3, <1 empty item> ]
*/

Indexed List

The array is an indexed list in which the first item is at index 0(zero). JavaScript array is dynamic which means it allows you to set a value at any index and grows automatically. To check the size of it, you can check the value of its length” property.

const numbers = [45, 12, 99, 2, 10, 78, 34];numbers.length; // 7

The “length” property is not a read-only property and changing its value will change the array size. This is the quickest way to truncate or clear an array.

const arr = [];arr.length; // 0arr.length = 5;arr.length; // 5
arr; // [ <5 empty items> ]

Creating array from something else

The Array comes with the static “from” method that takes an array-like or iterable object to create an array from.

const nSet = new Set([45, 12, 99]);const strArray = Array.from('sample');
const setArray = Array.from(nSet);
const setValsArray = Array.from(nSet.values()); // values iterator
const nArray = Array.from(12); // not iterable
const nullArray = Array.from(null); // TypeError: object null is not iterable
console.log(
strArray, // [ 's', 'a', 'm', 'p', 'l', 'e' ]
setValsArray, // [ 45, 12, 99 ]
setArray, // [ 45, 12, 99 ]
nArray, // []
)

The “from” method is an even more powerful and better alternative to create empty-sized arrays. Because it takes an array-like object, we can pass a literal object to it with a single “length” property that will be used to create an array of that size.

const months = Object.seal(Array.from({length: 12}));console.log(months)
/* prints
[
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined, undefined,
undefined, undefined
]
*/

The above example is an improvement to the example to create months we saw previously. It creates an array of size 12 with all the items as undefined which removes the need to use the “fill” method and works better with the Object “seal” method as well.

The power of the “from” method resides in its second argument. You can pass a map function that transforms the items before creating the array. We will talk about the map” method later on and show you how the “from” method can be superior in some cases.

const numbers = new Set([45, 12, 99]);
const numbersDoubled = Array.from(numbers, (n) => n * 2);
console.log(
numbersDoubled // [ 90, 24, 198 ]
)

It is perfect for when you have some iterable object and want an array from it but with the data transformed. It allows you to map non-array iterable objects.

Accessing, Adding, and Removing Items

The array is an indexed list so you can use the index to insert, access, and remove items you want. You can use the brackets notation to specify the index and it comes with some useful methods to make CRUD operations easier.

  • Accessing Items at Index:
    You can use the brackets notation with the index you want to grab the item at that specific index location. You can also use the “at” method which is the new member of the family and is a great way to get the last item.
const numbers = [45, 12, 99, 2, 10, 78, 34];console.log(
numbers[1], // 12
numbers[10], // undefined
numbers.at(-1) // 34 - last item
)
  • Setting and Updating Value at Index:
    To add or update an item, you assign a value to the index position in the array. JavaScript array is dynamic so if you set a value at an index beyond its size it will leave empty slots until the index you provided and then set your value which causes the array to increase size.
const numbers = [45, 12, 99];numbers[1] = 50;
numbers[5] = 100;
console.log(
numbers[1], // 50
numbers[5], // 100
numbers, // [ 45, 50, 99, <2 empty items>, 100 ]
numbers.length // 6
)
  • Adding Item at the End or Start:
    To add an item to the end of the array you use thepush” method and to add at the beginning you use the unshift” method. Both methods return the new array length and take single or comma-separated items to add.
const numbers = [45, 12, 99];numbers.push(100, 120);
numbers.unshift(40, 43);
console.log(
numbers, // [ 40, 43, 44, 45, 12, 99, 100, 120 ]
numbers.length // 8
)
  • Clearing Value at Index:
    To clear an item you make usage of thedelete” operator which leaves an empty slot but does not change the array size. It does not remove the item from the array, it just clears the value at that index.
const numbers = [45, 12, 99];delete numbers[1];console.log(
numbers, // [ 45, <1 empty item>, 99 ]
numbers.length // 3
)
  • Removing Item at the End or Start:
    To remove an item at the end of the array you can use the pop” method and to remove at the beginning you use the shift” method. Both methods return undefined if the array is empty otherwise the item removed.
const numbers = [45, 12, 99];numbers.pop(); // returns 99
numbers.shift(); // returns 45
console.log(
numbers, // [ 12 ]
numbers.length // 1
)
  • Removing or Adding Anywhere:
    The best way to add and remove items anywhere is to use the “splice” method. The reason I say “best” is because there are other ways to remove and items especially if you don't know the position.
const numbers = [45, 12, 99];numbers.splice(1, 0, 100, 200);
// adds 100 and 200 starting from index 1 and, returns []
// array becomes: [ 45, 100, 200 ]

The “splice” method takes the starting index as the first argument, the number of items to remove starting at that index as the second argument, and then comma-separated items to add.

Note: All arguments are optional and no arguments provided simply return a shallow copy of the array.

Only specifying the first argument will remove all items from that index.

numbers.splice(1); 
// remove all items from 1 to end, returns [ 12, 99 ]
// array becomes: [ 45 ]

Specifying the first and second argument will remove the number of items(second argument) starting from that index(first argument) inclusive.

numbers.splice(1, 1);
// remove 1 item at index 1 and returns [ 100 ]
// array becomes: [ 45, 200 ]

Specifying all 3 arguments will remove the needed items and then add new ones.

numbers.splice(1, 1, 300);
// removes 200 and adds 300 at index 1, returns [200]
// array becomes: [ 45, 300 ]

All these methods require you to specify the position to perform the insertion, removal, or update. There will be cases you don’t know the position or the size of the list and will require other solutions which we will learn about later on in this article.

Combining Arrays

Combining and splitting an array can also be a way to compose a new array, add, or remove items as well.

You can combine arrays using several approaches. The official and traditional way is by using the concat” method but it can be limiting at times depending on how you want the arrays combined.

const numbersA = [45, 12, 99];
const numbersB = [89, 34, 10];
const numbersC = [33, -1, 92];
const combinedArrays = numbersA.concat(numbersB, numbersC);console.log(combinedArrays)
/* prints
[
45, 12, 99, 89, 34,
10, 33, -1, 92
]
*/

The “concat” method is a pure method which means it will return a new array with all the arrays combined instead of changing the array where you call it from. It also combines the methods in the order you specify.

The same thing can be accomplished with the spread operator.

console.log([...numbersA, ...numbersB, ...numbersB])
/* prints
[
45, 12, 99, 89, 34,
10, 33, -1, 92
]
*/

We can also use the “push”, “shift” and “splice methods to combine array at a specific end or position but these perform the change in place so make sure this is the desired effect. The “concat” method and “spread operator” is your best option for pure new arrays.

// add items to the end
numbersA.push(...numbersB, ...numbersB);
// add items to the beggining
numbersA.unshift(...numbersB, ...numbersB);
// add items at index 2
numbersA.splice(2, 0, ...numbersB, ...numbersB);

Splitting Array

To split an array your best option is the slice” method which takes a start and end index to slice your array from. This method is also pure since it does not change the original array.

const numbers = [45, 12, 99, 89, 34, 10, 33, -1, 92];numbers.slice();
// returns a shallow copy of the array
// with all the items inside
numbers.slice(5);
// returns [ 10, 33, -1, 92 ]
// a new array with items starting from
// index 5(inclusive) to the end
numbers.slice(3, 7);
// returns [ 89, 34, 10, 33 ]
// a new array with items starting from
// index 3(inclusive) to 7(exclusive)
numbers.slice(-4);
// returns [ 10, 33, -1, 92 ]
// a new array with items starting from
// the end to minus 4 positions(inclusive)
// the 4 last items
console.log(numbers)
/* prints
[
45, 12, 99, 89, 34,
10, 33, -1, 92
]
*/

If you want to split an array in place you can use the “splice” method with the first 2 arguments where the first argument is the index to start from and the second is the number of items to remove which if omitted defaults to all items after the starting index.

const numbers = [45, 12, 99, 89, 34, 10, 33, -1, 92];numbers.splice(3, 4);
// returns [ 89, 34, 10, 33 ]
// a 4 items array
// starting from index 3(inclusive)
numbers.splice(-2);
// returns [ -1, 92 ]
// a 2 items array with the 2 last items
console.log(numbers)
/* prints
[ 45, 12, 99 ]
*/

The “split” method returns a portion of the array without changing the original and the “splice” will return a portion and change the original array.

Copying Array

We already know how to copy an array — at least the shallow option — by using the “slice” method call with no arguments or the spread operator. They are your best options to make a shallow copy of an array.

const numbers = [45, 12, 99, 2, 10, 78, 34];console.log(
numbers.slice(),
[...numbers],
numbers
)
/* all print
[
45, 12, 99, 2,
10, 78, 34
]
*/

If you want to deep clone an array you have to use some custom solution or a library like lodash.

There is a simple cloning solution I spoke about along with many others in my “25 JavaScript Tricks You Need To Know About” article you should check. This custom deep clone method will work with Array and on any object as well.

const people = [
{name: "John Doe", age: 22},
{name: "Jane Doe", age: 20}
];
const deepClone = obj => {
let clone = obj;
if (obj && typeof obj === "object") {
clone = obj.constructor
? new obj.constructor()
: Object.create(null);

Object.getOwnPropertyNames(obj).forEach(
prop => (clone[prop] = deepClone(obj[prop]))
);
}
return clone;
};
console.log(
deepClone(people),
people
)
/* all print
[
{name: "John Doe", age: 22},
{name: "Jane Doe", age: 20}
]
*/

A deep copy is excellent for arrays containing non-primitive items like objects. That ensures that if you change the item values that both arrays won't get the change.

const items = [{value: 12}, {value: 48}];const itemsCopy1 = [...items]; // shallow
const itemsCopy2 = deepClone(items);
itemsCopy1[0].value = 30;console.log(
itemsCopy1,
items
)
/* both receive the changes
[ {
value: 30 }, { value: 48 } ]
*/
console.log(
itemsCopy2
)
/* the copy is unique so it did not
receive the changes
[ { value: 12 }, { value: 48 } ]
*/

Bot, Array and TypedArray, come with the “copyWithin” method which allows you to copy items in the array to another location yet inside the array without changing its size. This is a perfect method to use with the TypedArray to move bytes around and it works like C++ “memmove”.

It takes 3 arguments where the first is the index of the place to copy the items to, followed by a start and end source index to copy the items from. The second and third arguments default to the start and end index of the array. The first argument defaults to 0(zero).

[1, 2, 3, 4, 5].copyWithin(-2)
// copies the array inside starting from index 3
// results in [1, 2, 3,
1, 2]

[1, 2, 3, 4, 5].copyWithin(0, 3)
// copies items from index 3(4 and 5)
// to position 0
// results in [
4, 5, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(0, 3, 4)
// copies items from index 3 to 4(exclusive)
// to position 0
// [
4, 2, 3, 4, 5]

[1, 2, 3, 4, 5].copyWithin(-2, -3, -1)
// copies items from index -3(3rd from the end)
// to -1(1 from the end) - (items 3 and 4)
// to position -2(2nd from the end)
// [1, 2, 3,
3, 4]

The “copyWithin” is a mutable method which means it changes the array in place even though it does not change the length of the array.

Iterating the Array and its Iterators

The JavaScript array is an iterable object which means it can easily be used with any looping options there is. It also contains various methods which allow you to loop the array and perform various actions and checks. The one we will use for this section is the “forEach” method.

const numbers = [45, 12, 99, 2, 10, 78, 34];for(const numb of numbers) {
console.log(numb); // prints numbers
}
for(const index in numbers) {
console.log(index); // prints indexes
}
numbers.forEach((numb, index) => console.log(numb, index))

The disadvantage of the “forEach” method is that you cannot break out of the loop as you would in a “for” or “while” loop by using the “break” keyword. You can return early which works like the continue” keyword inside the “for” and “while” loop. To break out of the loop you can use the some” method which Ill cover in the next article.

numbers.some(numb => {
if(numb > 80) return true;
// do something here return false;
});

The above example will loop and allow you to do something while a number is less than 80. That's is the equivalent of breaking out of a loop.

The array comes with 3 iterators you can use for much better control over the iteration. These are accessed by calling “keys”, “values” and “entries” methods. They work great with the “from” method and loops in general.

const numbers = [45, 12, 99, 2, 10, 78, 34];const doubleNumbers = nList => {
return Array.from(nList, n => n * 2);
}
for(const [index, value] of numbers.entries()) {
// do something
}
doubleNumbers(numbers.values())
/* prints
[
90, 24, 198, 4,
20, 156, 68
]
*
/

You should learn more about Iterators to understand the power of array iterators which allows you for a more fine and explicit iteration of the array. Iterators are not an exclusive thing to Arrays but it is what makes it possible for you to loop the array for its items.

Next

Let’s continue exploring arrays by learning how to search, sort, map, reduce, reverse, shuffle, filter, and much more in the next article.

YouTube Channel: Before Semicolon
Website: beforesemicolon.com

More content at plainenglish.io

Blog & YouTube Channel for Web, UI & Software Development - beforesemicolon.comyoutube.com/c/BeforeSemicolon

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store