Objectives

Array Basics, Array Methods & Iteration

Array Basics

Introduction

So far, we've seen five different primitive data types in JavaScript: string, number, boolean, null, and undefined. We've also seen how to store these values inside of variables.

Sometimes, however, you need a more complex data structure when building your application. For example, maybe you need a list of restaurant names so that you can display each one to a user when she's looking for a place to eat nearby. Or maybe you're doing some math and want to maintain a list of prime numbers. It would be pretty annoying to have to write

const firstPrime = 2;
const secondPrime = 3;
const thirdPrime = 5;
const fourthPrime = 7;
const fifthPrime = 11;

This is fine if you know how many primes you need at the outset, but what if you didn't know how many values you needed to store? Or what if you did know how many values you needed, but the number was quite large? Writing out a variable for each one can quickly become unmanageable.

Thankfully, JavaScript provides you with a data type to help in these situations: the array. You can think of an array as simply a list of values.

To write an array in JavaScript, you use square brackets [] and comma separate each value in the array. Here are some examples:

const primes = [2, 3, 5, 7, 11];
const names = ['Alice', 'Bob', 'Charlie'];
const booleans = [true, false, false, true];
const mixedTypes = [1, 'sweet', true, null, NaN, 'bye!'];
const woahhh = ['Whats up with this? -->', ['Woah', 'crazy!']];
const emptyArray = [];

You can put anything you want inside of an array: numbers (as in primes), strings (as in names), booleans (as in booleans), and other primitive types are all fair game. You can also have multiple different types inside of an array: just because the first element in an array is a number doesn't mean that every subsequent element needs to be a number too. For example, mixedTypes has many different types inside of it. You can even store arrays inside of other arrays, as in the woahhh array above!

At this point, you may be wondering why we didn't mention arrays when we talked about other data types in JavaScript. The reason is that up until now, we've been dealing with primitive data types in JavaScript. But arrays aren't primitives; they're examples of what's called a reference type. We'll talk about reference types in more detail in the next javascript lab. For now, it's sufficient to note that

console.log(typeof [1, 2, 3]);

prints object. So arrays are a type of object, which you used in gomix in last week's lab (more on objects later).

Accessing and updating array values

To access an element in an array, we specify the name of the array followed by sqqare brackets brackets and the position (also called the index) of the element we're trying to access. Arrays are zero-indexed, which means that the first element is accessed at index 0. Let's look at an example:

const arr1 = [5, 3, 10];
console.log(arr1[0]); // should equal 5
console.log(arr1[1]); // should equal 3
console.log(arr1[2]); // should equal 10
console.log(arr1[3]); // should be undefined -- remember, arrays are zero-indexed!
console.log(arr1[1 + 1]); // the same as arr[2], which is 10
console.log(arr1[arr1.length - 1]); // shorthand for the last element of an array, in this case 10

To update a value in an array, we can simply assign an element at a given index to a new value:

const arr2 = [5, 3, 10];
arr2[0] = -1000;
arr2[2] = 'dope';
console.log(arr2); // should be [-1000, 3, 'dope']

Adding to arrays

There are a number of ways you can add elements to an array.

One way is by setting a value at a new index in the array.

const arr3 = [1,2,3];
arr3[3] = 4;
console.log(arr3); // [1,2,3,4]

Be careful with this approach, though -- you can add an element at any index, and any elements that don't have values in them will be filled with undefined values.

const arr4 = [1,2,3];
arr4[5] = 'whoa';
console.log(arr4); // [1, 2, 3, undefined, undefined, 'woah']

If you want to add to the end of an array, a better approach is to use the push function - this function returns the new length (the number of elements) of the array.

const arr5 = [3, 2, 5];
arr5.push(7); 
console.log(arr5); // [3, 2, 5, 7]

On the other hand, if you want to add to the beginning of an array, you can use the unshift function. As with push, unshift returns the length of the modified array.

const arr6 = [1, 2, 3];
arr6.unshift(0);
console.log(arr6); // [0,1,2,3]

Removing from arrays

We've seen how we can add elements from arrays. But what about removing elements?

One (not common) way to remove elements is to manually set the length of the array to a number smaller than its current length. For example:

const arr7 = [1, 2, 3];
arr7.length = 2; // set the new length
console.log(arr7); // [1,2]

A more common way to remove elements from the back of an array is to use pop(). This function works in sort of the opposite way as push, by removing items one by one from the back of the array. Unlike push, however, pop doesn't return the length of the new array; instead, it returns the value that was just removed.

const arr8 = [1,2,3];
arr8.pop(); // returns 3
console.log(arr8); // [1,2]

If you want to remove an element from the front of an array, you should shift() (like unshift, but the opposite)! As with pop(), shift() returns the removed value.

const arr9 = [1,2,3];
arr9.shift();
console.log(arr9); // [2,3]

There's also a delete keyword in JavaScript, which you might think could be used to delete elements in an array. However, this isn't quite how delete works with arrays. When you use this keyword, the value at the index where you delete will simply be replaced by undefined. This usually isn't what you want, which is why you won't often see people use delete on arrays. It's more common to see this word used with objects, which we'll talk more about in the next unit.

const arr10 = [5, 4, 3, 2];
delete arr10[1];
console.log(arr10); // [5, undefined, 3, 2]

Exercises

For these exercises, create:

  • arrays-01.html
  • arrays-01.js

Use this contents for the html file:

arrays-01.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-01.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

And use the developer tools in chrome to run and monitor the script.

Exercise 1:

In arrays-01.js, create an array of your favorite foods (call it favoriteFoods). Make sure it has at least three elements.

  • Access the second element in favoriteFoods.
  • Change the last element in favoriteFoods to some other food.
  • Remove the first element in favoriteFoods and store it in a variable called formerFavoriteFood.
  • Add a favorite food to the back of the favoriteFoods array.
  • Add a favorite food to the front of the favoriteFoods array.

Exercise 2

What happens when you try to pop from an empty array? Create an empty array and see how chrome responds to this.

Array Methods

So far, we've seen how to access, update, add and remove items from an array. We've also encountered some common array methods, including push, pop, shift, unshift, and splice. But these aren't the only methods you're likely to encounter when working with arrays. Let's take a look at a few more.

Common array functions and properties

length

length returns how many elements are in the array. This is a property, NOT a function (you can tell because we type length, not length(). As we've seen, it can (but is almost never) be used to remove elements/clear an array.

const arr14 = [1, 2, 3, 4];
console.log(arr14.length); // 4
console.log(arr14[arr14.length]); // undefined
console.log(arr14[arr14.length - 1]); // 4 - this is a nice way to access the last element of an array when you don't know how many elements are inside it.

slice

slice makes a copy of an array. We can use it to copy the entire array, or create a copy of a subarray. If we just invoke slice() with no arguments, we'll create a copy:

const arr15 = [1, 2, 3, 4];
const copy = arr15.slice();
console.log(arr15); // [1,2,3,4];

Alternatively, you can pass in two arguments to slice. Like splice, the first argument indicates the starting index of the subarray you want. The second argument indicates the ending index. The subarray you get will consist of all the values starting from the starting index and going up to (but not including) the ending index:

const arr16 = [7, 6, 5, 4, 3, 2];
const copya = arr16.slice(1, 2);
console.log(copya); // [6]
const copyb = arr16.slice(2, 5);
console.log(copyb); // [5, 4, 3]
const copyc = arr16.slice(2, 1);
console.log(copyc); // []

concat

concat joins two arrays together.

const arr18 = [1, 2, 3];
const arr19 = [4, 5, 6];
const combined1 = arr18.concat(arr19);
console.log(combined1); // [1,2,3,4,5,6]

In fact, you can pass multiple arrays into concat and it will still return a single array to you:

const arr20 = ['a', 'b', 'c'];
const arr21 = ['d', 'e', 'f'];
const arr22 = ['g', 'h', 'i'];
const combined2 = arr20.concat(arr21, arr22);
console.log(combined2); // ['a','b','c','d','e','f','g','h','i'];

What's more, you don't even need to pass an array into concat! Any comma-separated list of values can be concatenated with the original array:

const openingWords = ['It', 'was', 'a'];
const moreOpeningWords = openingWords.concat('dark', 'and', 'stormy', 'night');
console.log(moreOpeningWords); // ['It', 'was', 'a', 'dark', 'and', 'stormy', 'night']

join

join joins elements of an array into a string separated by whatever you pass in as an argument to join. This argument is frequently referred to as a delimiter. Here are a couple of examples:

const arr23 = ['Hello', 'World'];
const combined3 = arr23.join(' '); // 'Hello World'
console.log(combined3);

var arr24 = ['I', 'have', 'a', 'big', 'announcement'];
const combined4 = arr24.join('! ') + '!'; // 'I! have! a! big! announcement!'
console.log(combined4);

indexOf

indexOf finds the first index of the element passed in (starting from the left). If the element is not found, it returns -1. Here are some examples:

const arr25 = [1, 2, 3, 4, 5, 4, 4];
console.log(arr25.indexOf(2)); // 1
console.log(arr25.indexOf(3)); // 2
console.log(arr25.indexOf(1)); // 0 - remember, arrays are zero indexed
console.log(arr25.indexOf(4)); // 3 - indexOf stops once it finds the first 4.
console.log(arr25.indexOf(10)); // -1

You'll see this function very commonly used to check if an element is in an array or not. Here's an example:

const moviesIKnow = [
  'Waynes World',
  'The Matrix',
  'Anchorman',
  'Bridesmaids',
];

const yourFavoriteMovie = prompt('Whats your favorite movie?');
if (moviesIKnow.indexOf(yourFavoriteMovie) > -1) {
  alert('Oh, cool, Ive heard of ' + yourFavoriteMovie + '!');
} else {
  alert('I havent heard of ' + yourFavoriteMovie + '. Ill check it out.');
}

lastIndexOf

lastIndexOf works just like indexOf, but starts searching from the end of the array rather than the beginning.

const arr26 = [1, 2, 3, 4, 5, 4, 4];
console.log(arr26.indexOf(4)); // 3
console.log(arr26.lastIndexOf(4)); // 6 - this one is different now as it starts from the end!
console.log(arr26.lastIndexOf(10)); // -1 - still returns -1 if the value is not found in the array

Exercises

For these exercises, create:

  • arrays-02.html
  • arrays-02.js

And use the developer tools in chrome to run and monitor the script.

arrays-02.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-02.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

Exercise 1:

In arrays-02.js, declare an empty array called me like this:

const me = [];

After writing code for each of the steps below, print out the me array like this:

console.log(me);
  • Add your first name to the me variable
  • Add your last name to the end of the me variable
  • Add your favorite color to the beginning of the me variable
  • Remove the favorite color from the me variable.
  • Create another array called other.
  • Add your favorite number to other
  • Add the string "JavaScript" to the end of the other variable

Exercise 2:

Declare the following array:

const programming = ['JavaScript', 'Python', 'Ruby', 'Java']

Then write the code to do this:

  • Create a new array modernProgramming, which combines programming with the array ['Haskell', 'Clojure'].
  • Return the string 'JavaScript, Python, Ruby, Java' by using join on the programming array.

Array Iteration: for

Very often, you'll want to access each element of an array in order and do something with each element. For example, maybe you have an array of tweets, and you want to show each one on the page. Or maybe you have a list of numbers that you want to apply some mathematical operation to.

For instance, suppose you have an array of numbers that you want to round to the nearest whole number:

const decimals = [1.1, 1.6, 2.8, 0.4, 3.5, 1.6];

One way to do this is to round each element individually using the built-in Math.round function:

decimals[0] = Math.round(decimals[0]);
decimals[1] = Math.round(decimals[1]);
decimals[2] = Math.round(decimals[2]);
decimals[3] = Math.round(decimals[3]);
decimals[4] = Math.round(decimals[4]);
decimals[5] = Math.round(decimals[5]);

Now if you look at decimals, you should see that it is equal to [1, 2, 3, 0, 4, 2]. Great! We've rounded all of our numbers.

But this approach isn't great. What if we have 100 numbers we want to round? Or 1,000? And what if we want to do something more complicated than simply round each one? The approach we've used here doesn't scale very well.

Thankfully, there's a better way to make your way through an array and do something with each element, through a process called iteration, or looping. Let's talk about iteration in general, and then see how we can apply it to arrays.

Iteration: For loops

One of the most common ways to loop is with a for loop. A for loop consists of three parts followed by a block of code inside of curly braces {}:

for (initializer, condition, counter) {}

initializer - this is where we can declare variables to be used in the loop. We usually declare a variable called i which will serve as a counter variable for the number of times that we should loop.

condition - this MUST be an expression that returns true or false. You can read this condition as "Keep looping as long as this condition is true."

counter - this is how we change the variables initialized (typically, either by increasing or decreasing them). We commonly increment variables by 1 using the ++ operator and decrement by 1 using --.

As long as the condition is true, the code inside the curly braces will run. After running, the counter expression will run, and then the condition will be checked again.

// start with a variable called i and set it to 0
// keep looping as long as i is less than 5
// at the end of each for loop, increase the value of i
for (let a = 0; a < 5; a++) {
  console.log(a);
}

// prints out:

// 0
// 1
// 2
// 3
// 4

What gets logged if you change i<5 to i<10? If you change i++ to i+=3? Experimenting with the initializer, condition, and counter is a great way to develop your intuition for for loops!

You can use a loop to iterate through an array in a similar fashion. In this case, typically i refers to the current index in the array, the condition tells the loop to continue until i equals the length of the array, and the counter increments i. Let's see how we could refactor our earlier rounding example to use a for loop:

const decimals2 = [1.1, 1.6, 2.8, 0.4, 3.5, 1.6];

for (let i = 0; i < decimals2.length; i++) {
    decimals2[i] = Math.round(decimals[i]);
}

Strings Revisited

Now that we've learned about arrays, let's briefly return to strings and compare and contrast these two data types. They do have some similarities, but it's important to understand their differences as well.

Looping over strings

Just like we can iterate over arrays (and objects), we can also iterate over strings! Since strings have a length property, we always know at what point to stop looping, just like with arrays. Let's see an example of looping over a string:

const name = 'elie';

for (let t = 0; t < name.length; t++) {
  console.log(name[t]);
}

// e
// l
// i
// e

Exercises

For these exercises, create:

  • arrays-03.html
  • arrays-03.js

And use the developer tools in chrome to run and monitor the script.

arrays-03.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-03.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

Exercises 1: Array iteration

Introduce this array:

const people = ['Greg', 'Mary', 'Devon', 'James'];
  • Using a loop, iterate through this array and console.log all of the people.
  • Again using a loop, iterate in the reverse direction, logging the people starting at 'James' and finishing with 'Greg'

Exercises 1: Array mutation.

Using the same array as in exercise 1:

  • Write the code to remove "Greg" from the array.
  • Write the code to remove "James" from the array.
  • Write the code to add "Matt" to the front of the array.
  • Write the code to add your name to the end of the array.

Array Iteration: While loops

Along with for loops, we can also use a while loop. Unlike for loops, while loops only take a condition. This means that you need to handle initialization before the loop, and incrementing/decrementing yourself inside of the loop. If you forget to increment/decrement inside the loop, the loop will never terminate! Instead, you'll be stuck in what's called an infinite loop!

Here's an example of a working while loop:

let i = 0;
while (i < 5) {
  console.log(i);
  i++;
}

Here's how we could rewrite our rounding example to use a while loop:

let decimals3 = [1.1, 1.6, 2.8, 0.4, 3.5, 1.6];
let j = 0;

while (j < decimals.length) {
  decimals3[j] = Math.round(decimals[j]);
  j++;
}

Iteration: Do While Loops

Similar to while loops, we can also write do...while loops, which specify our condition at the end. Here is an example:

let k = 0;
do {
  console.log(k);
  k++;
} while (k < 5);

The main difference between a while loop and a do...while loop is that the code inside of a do...while loop is guaranteed to execute at least once. For example:

let m = 0;
while (m < 0) {
  console.log(m);
  m++;
}

// nothing is logged, since 0 < 0 is false

let n = 0;
do {
  console.log(n);
  n++;
} while (n < 0);

// 0 gets logged, since the code inside the block runs once
// before the while condition is checked

Here's how we could rewrite our rounding example to use a do...while loop:

const decimals4 = [1.1, 1.6, 2.8, 0.4, 3.5, 1.6];
var p = 0;

do {
  decimals4[p] = Math.round(decimals4[p]);
  p++;
} while (p < decimals4.length);

Exiting out of loops

Sometimes we want to exit a loop before it has finished. To do that, we use the word break

for (let q = 0; q < 5; q++) {
  if (Math.random() > 0.5) {
    console.log('Breaking out of the loop when q is ' + q);
    break;
  }
  else {
    console.log(i);
  }
}

We can also skip the current iteration and continue the loop at the next step in the itration by using the word continue

for (let r = 0; r < 5; r++) {
  if (Math.random() > 0.5) {
    console.log('Skipping the console.log when i is ' + r);
    continue;
  }
  console.log(i);
}

Exercises

For these exercises, create:

  • arrays-04.html
  • arrays-04.js

And use the developer tools in chrome to run and monitor the script.

arrays-04.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-04.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

Exercise 1: Iteration

Using this array:

const people = ['Greg', 'Mary', 'Devon', 'James'];
  • Using a loop, iterate through people array and after logging "Mary", exit from the loop.
  • Write code to make a copy of the array
  • Write the code that gives the index of where "Mary" is located.
  • Write the code that gives the index of where "Foo" is located (this should return -1).

Arrays of Objects

Create a file called arrays3.js declare this array:

const songList = [
  'Piano Sonata No. 3',
  'Piano Sonata No. 7',
  'Piano Sonata No. 10',
];

console.log(songList);

We can write a loop to print out the elements individually:

for (let i = 0; i < songList.length; i++) {
  console.log('Song ' + i + ':' + songList[i]);
}

This should render as:

Song 0:Piano Sonata No. 3
Song 1:Piano Sonata No. 7
Song 2:Piano Sonata No. 10

What if we wanted to record both the song and the artist? We could try this:

const songList2 = [
  'Piano Sonata No. 3',
  'Beethoven',
  'Piano Sonata No. 7',
  'Beethoven',
  'Piano Sonata No. 10',
  'Beethoven',
];

for (let k = 0; k < songList2.length; k++) {
  console.log('Song ' + k + ':' + songList2[k]);
}

This code outputs the array to the console like this:

Song 0:Piano Sonata No. 3
Song 1:Beethoven
Song 2:Piano Sonata No. 7
Song 3:Beethoven
Song 4:Piano Sonata No. 10
Song 5:Beethoven

However, this is very inconvenient and difficult to manage. The output above does not really represent our intention. We could try this:

for (let k = 0; k < songList2.length; k=k+2) {
  console.log('Song ' + k + ': Title: ' + songList2[k] + ': Artist: ' + songList2[k + 1]);
}

Which outputs:

Song 0: Title: Piano Sonata No. 3: Artist: Beethoven
Song 2: Title: Piano Sonata No. 7: Artist: Beethoven
Song 4: Title: Piano Sonata No. 10: Artist: Beethoven

Closer to our intention - but still not satisfactory (the Song numbers look wrong).

Instead of an array of strings, we need an array of songs, with each song containing a title + artist:


const songList3 = [
  {
    title: 'Piano Sonata No. 3',
    artist: 'Beethoven',
  },
  {
    title: 'Piano Sonata No. 7',
    artist: 'Beethoven',
  },
  {
    title: 'Piano Sonata No. 10',
    artist: 'Beethoven',
  },
];

Look carefully at the above - we are defining an array as before, but each array is an object containing two fields:

  • title
  • artist

We can now output the play list more conveniently:

for (let j = 0; j < songList3.length; j++) {
  console.log('Song ' + j + ': Title: ' + songList3[j].title + ': Artist: ' + songList3[j].artist);
}

Note how we access each element:

songList3[j].title  ... songList3[j].artist

Each element is an object, so we can access each attribute using '.' and then the name of the attribute.

Exercises

For these exercises, create:

  • arrays-05.html
  • arrays-05.js

Use this contents for the html file:

arrays-05.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-05.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

Exercise 1:

Extend songList3 to include a 'duration' field for each song. This should be a simple number. Have the this number included in the log when the list is printed out.

Exercise 2:

Output the total number of songs when all song details have been printed out.

Exercise 3:

Along with the total number of songs, see if you can also print out the total duration. This should be a simple total of the duration of all individual songs.

Nested Arrays

Here is a more complex datastructure, representing multiple playlists:

const playlistCollection = [
  {
    id: "01",
    title: "Beethoven Sonatas",
    songs: [
      {
        id: "04",
        title: "Piano Sonata No. 3",
        artist: "Beethoven",
      },
      {
        id: "05",
        title: "Piano Sonata No. 7",
        artist: "Beethoven",
      },
      {
        id: "06",
        title: "Piano Sonata No. 10",
        artist: "Beethoven",
      }
    ]
  },
  {
    id: "02",
    title: "Beethoven Concertos",
    songs: [
      {
        id: "07",
        title: "Piano Concerto No. 0",
        artist: "Beethoven",
      },
      {
        id: "08",
        title: "Piano Concerto No. 4",
        artist: "Beethoven",
      },
      {
        id: "09",
        title: "Piano Concerto No. 6",
        artist: "Beethoven",
      }
    ]
  },
  {
    id: "03",
    title: "Beethoven Variations",
    songs: [
      {
        id: "10",
        title: "Opus 34: Six variations on a theme in F major",
        artist: "Beethoven",
      },
      {
        id: "11",
        title: "Opus 120: Thirty-three variations on a waltz by Diabelli in C majo",
        artist: "Beethoven",
      }
    ]
  }
];

Exercises

For these exercises, create:

  • arrays-06.html
  • arrays-06.js

Use this contents for the html file:

arrays-06.html

<html> 
  <head>
    <title>JavaScript Test Site</title>
    <script src="arrays-06.js"></script>
  </head>
  <body>
    <p>Nothing going on yet.</p>
  </body>
</html>

Exercise 1:

Try the following - and observe the output in the console:

console.log (playlistCollection);

Exercise 2:

Comment out the above, and try this instead:

for (let i = 0; i < playlistCollection.length; i++) {
  console.log(playlistCollection[i]);
}

Compare the output form this with the previous version.

Exercise 3:

Comment out the above, and try this instead:

for (let i = 0; i < playlistCollection.length; i++) {
  console.log(playlistCollection[i].title)
}

Compare the output form this with the previous version.

Exercise 4:

These last two are more complex - try each on in turn and compare the console output:

for (let i = 0; i < playlistCollection.length; i++) {
  console.log(playlistCollection[i].title)
  for (let j=0; j < playlistCollection[i].songs.length; j++) {
    console.log(playlistCollection[i].songs[j]);
  }
}
for (let i = 0; i < playlistCollection.length; i++) {
  console.log(playlistCollection[i].title)
  for (let j=0; j < playlistCollection[i].songs.length; j++) {
    console.log(playlistCollection[i].songs[j].title);
    console.log(playlistCollection[i].songs[j].artist);
  }
}