Js Data Structures Arrays Readme

Problem Statement

We've talked about data types in JavaScript. But what if we need a way of organizing data? We'll need to reach for a data structure, and one of the most useful data structures is an Array.

Objectives

  1. Identify data structures and Arrays
  2. Create Arrays
  3. Access the elements in an Array
  4. Add elements to an Array
  5. Remove elements from an Array
  6. Replace elements in an Array
  7. Identify nested Arrays

Identify Data Structures and Arrays

A data structure is a means for associating and organizing information. Outside of the programming world, we use data structures all the time. For example, we might have a shopping list of the items we need to buy on our next grocery run or an address book for organizing contact information.

If we have a lot of related data, it's best to represent it in a related system. Imagine that we're working on a lottery application that has to represent the winning lottery numbers. We could do that as follows:

const firstNumber = 32;
const secondNumber = 9;
const thirdNumber = 14;
const fourthNumber = 33;
const fifthNumber = 48;
const powerBall = 5;

We've represented all six pieces of data, but they aren't related in any meaningful way. Every single time we want to reference that combination of winning numbers, we need to remember and type out six different variable names:

const firstNumber = 32;
const secondNumber = 9;
const thirdNumber = 14;
const fourthNumber = 33;
const fifthNumber = 48;
const powerBall = 5;

function logWinningNumbers (first, second, third, fourth, fifth, power) {
  console.log('Winning numbers:', first, second, third, fourth, fifth, power);
}

logWinningNumbers(firstNumber, secondNumber, thirdNumber, fourthNumber, fifthNumber, powerBall);
// LOG: Winning numbers: 32 9 14 33 48 5
// => undefined

That's so much typing! There are much, much better ways to organize data in JavaScript. Let's learn about one of the most common: the Array.

Create Arrays

An Array is a list, with the items listed in a particular order, surrounded by square brackets ([]):

['This', 'is', 'an', 'array', 'of', 'strings.'];
// => ["This", "is", "an", "array", "of", "strings."]

The members or elements in an Array can be data of any type in JavaScript:

['Hello, world!', 42, null, NaN];
// => ["Hello, world!", 42, null, NaN]

NOTE: In some other languages Arrays cannot be of multiple types. In C, C++, Java, Swift, and others you cannot mix types. JavaScript, Python, Ruby, Lisp, and others permit this.

Arrays are ordered, meaning that the elements in them will always appear in the same order. The Array [1, 2, 3] is different from the Array [3, 2, 1].

Just like any other type of JavaScript data, we can assign an Array to a variable:

const primeNumbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37];

const tvShows = ['Game of Thrones', 'True Detective', 'The Good Wife', 'Empire'];

We can find out how many elements an Array contains by checking the Array's built-in length property:

const myArray = ['This', 'array', 'has', 5, 'elements'];

myArray.length;
// => 5

We defined the above Arrays using the array literal syntax — that is, we literally typed out the Array that we wanted to create, square brackets and all. There are other ways to create new Arrays, but they are only necessary for very rare circumstances. For now, use Array literals.

To get a sense of just how effective Arrays are at keeping data organized, let's rewrite our lottery code to use an Array:

const winningNumbers = [32, 9, 14, 33, 48, 5];

function logWinningNumbers (numbers) {
  console.log('Winning numbers:', numbers);
}

logWinningNumbers(winningNumbers);
// LOG: Winning numbers: [32, 9, 14, 33, 48, 5]
// => undefined

The Array provides organization, and we only have to remember one identifier (winningNumbers) instead of six (firstNumber, secondNumber, and so on). We can also call Arrays expressive because putting all the winning numbers in a shared data structure communicates to other programmers "Hey, these things go together" in a way that firstNumber, secondNumber, etc. does not.

The one benefit of storing all six lottery numbers separately is that we had a really easy way to access each individual number. For example, we could just reference powerBall to grab the sixth number. Luckily, Arrays offer an equally simple syntax for accessing individual members.

Access the Elements in an Array

Every element in an Array is assigned a unique index value that corresponds to its place within the collection. The first element in the Array is at index 0, the fifth element at index 4, and the 428th element at index 427.

To access an element, we use the computed member access operator, which, conveniently enough, looks exactly like an Array:

const winningNumbers = [32, 9, 14, 33, 48, 5];
// => undefined

winningNumbers[0];
// => 32

winningNumbers[3];
// => 33

NOTE: Most people just call it bracket notation or the bracket operator, so don't worry too much about remembering the term computed member access operator.

Let's take a minute to think about how we could access the last element in any Array.

If myArray contains 10 elements, the final element will be at myArray[9]. If myArray contains 15000 elements, the final element will be at myArray[14999]. So the index of the final element is always one less than the number of elements in the Array. If only we had an easy way to figure out how many elements are in the Array...

const alphabet = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'];
// => undefined

alphabet.length;
// => 26

alphabet[alphabet.length - 1];
// => "z"

This is why it's called the computed member access operator. We put an expression (alphabet.length - 1) inside the square brackets, and the JavaScript engine computed the value of that expression to determine which element we were trying to access. In this case, alphabet.length - 1 evaluated to 25, so alphabet[alphabet.length - 1] became alphabet[25].

Add Elements to an Array

JavaScript allows us to manipulate the members in an Array in a number of ways.

.push() and .unshift()

With the .push() method, we can add elements to the end of an Array:

const superheroes = ['Catwoman', 'She-Hulk', 'Jessica Jones'];

superheroes.push('Wonder Woman');
// => 4

superheroes;
// => ["Catwoman", "She-Hulk", "Jessica Jones", "Wonder Woman"]

We can also .unshift() elements onto the beginning of an Array:

const cities = ['New York', 'San Francisco'];

cities.unshift('Los Angeles');
// => 3

cities;
// => ["Los Angeles", "New York", "San Francisco"]

Notice that the value returned by both methods is the length of the updated Array.

Destructive vs. Nondestructive

Both .push() and .unshift() update or mutate the original Array, adding elements directly to it. Operations that modify the original collection are destructive, and those that leave the original collection intact are nondestructive.

Mutating the original Array isn't necessarily a bad thing, but there's also a way to add elements nondestructively, leaving the original Array intact.

Spread Operator

ES2015 introduced the spread operator, which looks like an ellipsis: .... The spread operator allows us to spread out the contents of an existing Array into a new Array, adding new elements but preserving the original:

const coolCities = ['New York', 'San Francisco'];

const allCities = ['Los Angeles', ...coolCities];

coolCities;
// => ["New York", "San Francisco"]

allCities;
// => ["Los Angeles", "New York", "San Francisco"]

We created a new Array instead of modifying the original one — our coolCities Array was untouched. We can also use the spread operator to add a new item to the end of an Array without modifying the original:

const coolCats = ['Hobbes', 'Felix', 'Tom'];

const allCats = [...coolCats, 'Garfield'];

coolCats;
// => ["Hobbes", "Felix", "Tom"]

allCats;
// => ["Hobbes", "Felix", "Tom", "Garfield"]

Remove Elements from an Array

As complements for .push() and .unshift(), respectively, we have .pop() and .shift().

.pop() and .shift()

The .pop() method removes the last element in an Array, destructively updating the original Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.pop();
// => "Sun"

days;
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

The .shift() method removes the first element in an Array, also mutating the original:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.shift();
// => "Mon"

days;
// => [Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

Notice that the return value for the .pop() and .shift() methods is the element that was removed.

.slice()

To remove elements from an Array nondestructively (without manipulating the original Array), we can use the .slice() method. Just as the name implies, the .slice() method returns a portion, or slice, of an Array.

With No Arguments

If we don't provide any arguments, .slice() will return a copy of the original Array with all elements intact:

const primes = [2, 3, 5, 7];

const copyOfPrimes = primes.slice();

primes;
// => [2, 3, 5, 7]

copyOfPrimes;
// => [2, 3, 5, 7]

Note that the Array returned by .slice() has the same elements as the original, but it's a copy — the two Arrays point to different objects in memory. If you add an element to one of the Arrays, it does not get added to the other:

const primes = [2, 3, 5, 7];

const copyOfPrimes = primes.slice();

primes.push(11);
// => 5

primes;
// => [2, 3, 5, 7, 11]

copyOfPrimes;
// => [2, 3, 5, 7]

With Arguments

We can provide two arguments to .slice(), the index where the slice should begin and the index before which it should end:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(2, 5);
// => ["Wed", "Thu", "Fri"]

If no second argument is provided, the slice will run from the index specified by the first argument to the end of the Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(5);
// => ["Sat", "Sun"]

To remove the first element and return a new Array, we call .slice(1):

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(1);
// => ["Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

And we can remove the last element in a way that will look familiar from earlier in this lesson:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(0, days.length - 1);
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]

In fact, .slice() provides an easier syntax for grabbing the last element in an Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.slice(-1);
// => ["Sun"]

days.slice(-3);
// => ["Fri", "Sat", "Sun"]

When we provide a negative index, the JavaScript engine knows to start counting from the last element in the Array instead of the first.

.splice()

While .slice() allows us to return a piece of an Array without mutating the original (nondestructive), .splice() performs destructive actions. Let's familiarize ourselves with the MDN documentation:

`Array.prototype.splice()` documentation on MDN

The documentation shows three ways to use .splice():

array.splice(start)
array.splice(start, deleteCount)
array.splice(start, deleteCount, item1, item2, ...)

With a Single Argument

array.splice(start)

The first argument expected by .splice() is the index at which to begin the splice. If we only provide the one argument, .splice() will destructively remove a chunk of the original Array beginning at the provided index and continuing to the end of the Array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];

days.splice(2);
// => ["Wed", "Thu", "Fri", "Sat", "Sun"]

days;
// => ["Mon", "Tue"]

Notice that .splice() returns the removed chunk and leaves the remaining elements in the original Array.

With a negative 'start' index, the starting position counts backward from the end of the array:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

days.splice(-2);
// => ["Sat", "Sun"]

days;
// => ["Mon", "Tue", "Wed", "Thu", "Fri"]

With Two Arguments

array.splice(start, deleteCount)

When we provide two arguments to .splice(), the first is still the index at which to begin splicing, and the second dictates how many elements we want to remove from the Array. For example, to remove 3 elements, starting with the element at index 2:

const days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'];
// => ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

days.splice(2, 3);
// => ["Wed", "Thu", "Fri"]

days;
// => ["Mon", "Tue", "Sat", "Sun"]

Replace Elements in an Array

.splice() with 3+ arguments

array.splice(start, deleteCount, item1, item2, ...)

After the first two, every additional argument passed to .splice() will be inserted into the Array at the position indicated by the first argument. We can replace a single element in an Array as follows, discarding a card and drawing a new one:

const cards = ['Ace of Spades', 'Jack of Clubs', 'Nine of Clubs', 'Nine of Diamonds', 'Three of Hearts'];

cards.splice(2, 1, 'Ace of Clubs');
// => ["Nine of Clubs"]

cards;
// => ["Ace of Spades", "Jack of Clubs", "Ace of Clubs", "Nine of Diamonds", "Three of Hearts"]

Or we can remove two elements and insert three new ones as our restaurant expands its vegetarian options:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

menu.splice(1, 2, 'Veggie Burger', 'House Salad', 'Teriyaki Tofu');
// => ["Cheeseburger", "Fish and Chips"]

menu;
// => ["Jalapeno Poppers", "Veggie Burger", "House Salad", "Teriyaki Tofu", "French Fries", "Onion Rings"]

We aren't required to remove anything with .splice() — we can use it to insert elements anywhere within an Array. Here we're adding new books to our library in alphabetical order:

const books = ['Bleak House', 'David Copperfield', 'Our Mutual Friend'];

books.splice(2, 0, 'Great Expectations', 'Oliver Twist');
// => []

books;
// => ["Bleak House", "David Copperfield", "Great Expectations", "Oliver Twist", "Our Mutual Friend"]

Notice that .splice() returns an empty Array when we provide a second argument of 0. This makes sense because the return value is the set of elements that were removed, and we're telling it to remove 0 elements.

Keep playing around with .splice() in your browser's console to get comfortable with it.

Using the Computed Member Access Operator to Replace Elements

If we only need to replace a single element in an Array, there's a simpler solution than .splice():

const cards = ['Ace of Spades', 'Jack of Clubs', 'Nine of Clubs', 'Nine of Diamonds', 'Three of Hearts'];

cards[2] = 'Ace of Clubs';
// => "Ace of Clubs"

cards;
// => ["Ace of Spades", "Jack of Clubs", "Ace of Clubs", "Nine of Diamonds", "Three of Hearts"]

However, using the computed member access operator ([]) is still destructive — it modifies the original Array. There's a nondestructive way to replace or add items at arbitrary points within an Array, and it involves two of the concepts we learned earlier.

Slicing and Spreading

Combining .slice() and the spread operator allows us to replace elements nondestructively, leaving the original Array unharmed:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

const newMenu = [...menu.slice(0, 1), 'Veggie Burger', 'House Salad', 'Teriyaki Tofu', ...menu.slice(3)];

menu;
// => ["Jalapeno Poppers", "Cheeseburger", "Fish and Chips", "French Fries", "Onion Rings"]

newMenu;
// => ["Jalapeno Poppers", "Veggie Burger", "House Salad", "Teriyaki Tofu", "French Fries", "Onion Rings"]

Play around with this a bit until it makes sense. It's the trickiest thing that we've encountered in this lesson, so don't sweat it if it takes a little while to sink in!

Identify Nested Arrays

In the above 'slicing and spreading' example, if we don't use the spread operator we're left with an interesting result:

const menu = ['Jalapeno Poppers', 'Cheeseburger', 'Fish and Chips', 'French Fries', 'Onion Rings'];

const newMenu = [menu.slice(0, 1), 'Veggie Burger', 'House Salad', 'Teriyaki Tofu', menu.slice(3)];

newMenu;
// => [["Jalapeno Poppers"], "Veggie Burger", "House Salad", "Teriyaki Tofu", ["French Fries", "Onion Rings"]]

Holy nested Arrays, Batman!

Is this the array or the array within an array?

That's right — an Array can contain elements of any data type, including other Arrays:

const egregiouslyNestedArray = ['How', ['deep', ['can', ['we', ['go', ['?'], 'Pretty'], 'dang'], 'deep,'], 'it'], 'seems.'];

Pop that into your browser's JS console and check out the nesting:

`egregiouslyNestedArray` in the JS console

It's great that Arrays allow us to store other Arrays inside them, but this is a terrible way to represent a deeply nested data structure. In general, try to keep your Arrays to no more than two levels deep. Two levels is perfect for representing two-dimensional things like a tic-tac-toe board:

const board = [
  ['X', 'O', ' '],
  [' ', 'X', 'O'],
  ['X', ' ', 'O']
];

board;
// => [["X", "O", " "], [" ", "X", "O"], ["X", " ", "O"]]

The cool thing about representing a game board like that is in how we can access the different squares by specifying coordinates. The first [] operator grabs the row that we want, top (board[0]), middle (board[1]), or bottom (board[2]). For example:

board[1];
// => [" ", "X", "O"]

The second [] operator specifies the square within that row, left (board[1][0]), middle (board[1][1]), or right (board[1][2]). For example:

board[0][0];
// => "X"

board[0][2];
// => " "

board[2][2];
// => "O"

Effectively, we're using X and Y coordinates to refer to data within a two- dimensional structure.

Conclusion

We dove into data structures and the Array, including how to create Arrays, access elements in an Array, add elements to an Array, remove elements from an Array and replace elements in an Array. We also covered the difference between destructive and non-destructive Array manipulation.

Resources

Unlock your future in tech
Learn to code.

Learn about Flatiron School's Mission

With a new take on education that falls somewhere between self-taught prodigy and four-year computer science degree, the Flatiron School promises to turn students with little programming experience into developers.

In the six months since the Manhattan coding school was acquired by WeWork, it has spawned locations in Washington, D.C., Brooklyn, and London. Now, WeWork is opening a fourth Flatiron School location, this time in Houston.

Adam Enbar, Flatiron School's cofounder, believes now is the time to grow. "How the world is changing has impacted working and learning in very similar ways. We think education fundamentally is about one thing: enabling people to pursue a better life."

Learn. Love. Code.
Students come to Flatiron School to change their lives. Join our driven community of career-changers and master the skills you need to become a software engineer or a data scientist.
Find Us