Chapter 3

Arrays

In Chapter 2, we saw that strings can be thought of as sequences of characters in a particular order. In this chapter, we’ll learn about the array data type, which is the general JavaScript container for arbitrary elements in a particular order. We’ll start by explicitly connecting strings and arrays via the String split method (Section 3.1), and then learn about various array methods throughout the rest of the chapter.

3.1 Splitting

So far we’ve spent a lot of time understanding strings, and there’s a natural way to get from strings to arrays via the split method:

> "ant bat cat".split("  ");     // Split a string into a three-element array.
[ 'ant', 'bat', 'cat' ]

We see from this result that split returns a list of the strings that are separated from each other by a space in the original string.

Splitting on space is one of the most common operations, but we can split on nearly anything else as well:

> "ant,bat,cat".split(",");
[ 'ant', 'bat', 'cat' ]
> "ant, bat, cat".split(", ");
[ 'ant', 'bat', 'cat' ]
> "antheybatheycat".split("hey");
[ 'ant', 'bat', 'cat' ]

We can even split a string into its component characters by splitting on the empty string:

> "badger".split("")
[ 'b', 'a', 'd', 'g', 'e', 'r' ]

We’ll put this basic technique to good use in Section 5.3 (and we’ll also discover it has an important limitation).

Finally, it’s worth noting that splitting supports regular expressions, which are covered in Section 4.3.

3.1.1 Exercises

  1. Assign a to the result of splitting the string “A man, a plan, a canal, Panama” on comma-space. How many elements does the resulting array have?

  2. Can you guess the method to reverse a in place? (Google around if necessary.)

3.2 Array Access

Having connected strings with arrays via the split method, we’ll now discover a second close connection as well. Let’s start by assigning a variable to an array of characters created using split:

> let a = "badger".split("");

We can access particular elements of a using a bracket notation that’s common to a huge number of different languages, as seen in Listing 3.1.

Listing 3.1: Array access with the bracket notation.

> a[0];
'b'
> a[1];
'a'
> a[2];
'd'

Does Listing 3.1 look a little familiar? It’s the same basic relationship between characters and numerical index that we saw with the String#charAt method in Listing 2.16. (The notation in the previous sentence indicates that charAt is an instance method, i.e., a method on string instances.) In fact, the bracket notation actually works directly on strings:

> "badger"[0];
'b'
> "badger"[1];
'a'

We see from Listing 3.1 that, as with strings, arrays are zero-offset, meaning that the “first” element has index 0, the second has index 1, and so on. This convention can be confusing, and in fact it’s common to refer to the initial element for zero-offset arrays as the “zeroth” element as a reminder that the indexing starts at 0. This convention can also be confusing when using multiple languages (some of which start array indexing at 1), as illustrated in the xkcd comic strip “Donald Knuth” (https://m.xkcd.com/163/).1

1. This particular xkcd strip takes its name from renowned computer scientist Donald Knuth (pronounced “kuh-NOOTH”), author of The Art of Computer Programming and creator of the TEX typesetting system used to prepare many technical documents, including this one.

So far we’ve dealt exclusively with arrays of characters, but JavaScript arrays can contain all kinds of elements:

> a = ["badger", 42, soliloquy.includes("To be")];
[ 'badger', 42, true ]
> a[2];
true
> a[3];
undefined

We see here that the square bracket access notation works as usual for an array of mixed types, which shouldn’t come as a surprise. We also see that trying to access an array index outside of the defined range returns undefined (a value which we saw before in the context of console.log (Figure 2.4)). This might be a surprise if you have previous programming experience, since many languages raise an error if you try to access an element that’s out of range, but JavaScript is more tolerant in this regard.

3.2.1 Exercises

  1. Write a for loop to print out the characters obtained from splitting “honey badger” on the empty string.

  2. See if you can guess the value of undefined in a boolean context. Use !! to confirm.

3.3 Array Slicing

In addition to supporting the bracket notation described in Section 3.2, JavaScript supports a technique known as array slicing for accessing multiple elements at a time. In anticipation of learning to sort in Section 3.4, let’s redefine our array a to have purely numerical elements:

> a = [42, 8, 17, 99];
[ 42, 8, 17, 99 ]

The simplest way to slice an array is to provide only one argument, which returns all the elements in the array from that index on. For example, for an array with four elements, slice(1) returns the second, third, and fourth ones (recall that the “first” or zeroth element has index 0):

> a.slice(1);
[ 8, 17, 99 ]

We can also slice from one index to another:

> a.slice(1, 3);
[ 8, 17 ]

Slicing gives us an easy way to perform a common task, which is to access the last element in an array. Arrays, like strings, have a length property, so we could find the last element like this:

> a.length;
4
> a[a.length-1];
99

This can get a little messy if the variable name is long, though, which often happens in bigger projects that have lots of variables:

> let aMuchLongerArrayName = a;
> aMuchLongerArrayName[aMuchLongerArrayName.length - 1];
99

This leads us to a second method for picking off the last element, which is to use slice with a negative number, which counts from the end:

> aMuchLongerArrayName.slice(-1);
[ 99 ]

This is an array with one element, so we can select the element itself using the bracket notation:

> aMuchLongerArrayName.slice(-1)[0];
99

A final common case is where we want to access the final element and remove it at the same time. We’ll cover the method for doing this in Section 3.4.2.

3.3.1 Exercises

  1. Define an array with the numbers 1 through 10. Use slicing and length to select the third element through the third-to-last. Accomplish the same task using a negative index.

  2. Show that strings also support the slice method by selecting just bat from the string "ant bat cat". (You might have to experiment a little to get the indices just right.)

3.4 More Array Methods

In addition to the slice method seen in Section 3.3, arrays respond to a wealth of other methods. As usual, the documentation (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array) is a good place to go for details.

As with strings, arrays respond to an includes method to test for element inclusion:

> a;
[ 42, 8, 17, 99 ]
> a.includes(42);       // Test for element inclusion.
true
> a.includes("foo");
false

3.4.1 Sorting and Reversing

You can also sort an array in place—an excellent trick that in ye olden days of C often required a custom implementation. In JavaScript, we just call sort():

> a.sort();
[ 17, 42, 8, 99 ]
> a;                     // `a` has changed as the result of `sort()`.
[ 17, 42, 8, 99 ]

You might notice something strange here, which is that JavaScript has sorted the elements of the array not according to their numerical values, but rather “alphabetically”, so that 17 comes before 8 because 1 comes before 8 in the ordering scheme (ASCII) used by computers. (We’ll learn how to sort arrays numerically in Chapter 5.)

Another useful method—one we’ll put to good use in developing our palindrome theme starting in Section 5.3—is the reverse method:

> a.reverse();
[ 99, 8, 42, 17 ]
> a;                  // Like `sort()`, `reverse()` mutates the array.
[ 99, 8, 42, 17 ]

As noted in the comment, methods like a.sort() and a.reverse() mutate the array, meaning they modify it as a side effect of performing their respective actions. This is the sort of behavior that varies from one programming language to the next, so be careful when using similar methods in other languages.

3.4.2 Pushing and Popping

One useful pair of array methods is push and pop; push lets us append an element to the end of an array, while pop removes it:

> a.push(6);                 // Pushing onto an array (returns new length)
5
> a;
[ 99, 8, 42, 17, 6 ]
> a.push("foo");
6
> a;
[ 99, 8, 42, 17, 6, 'foo' ]
> a.pop();                   // `pop` returns the value itself
'foo'
> a.pop();
6
> a;
[ 99, 8, 42, 17 ]

As noted in the comments, pop returns the value of the final element (while removing it as a side effect), but push (somewhat counter-intuitively) returns the length of the new array. As of this writing, I don’t know why (and neither does Stack Overflow).

We are now in a position to appreciate the comment made in Section 3.3 about obtaining the last element of the array, as long as we don’t mind mutating it:

> let lastElement = a.pop();
> lastElement;
17
> a;
[ 99, 8, 42 ]
> let theAnswerToLifeTheUniverseAndEverything = a.pop();

3.4.3 Undoing a Split

A final example of an array method, one that brings us full circle from Section 3.1, is join. Just as split splits a string into array elements, join joins array elements into a string (Listing 3.2).

Listing 3.2: Different ways to join.

> a = ["ant", "bat", "cat", 42];
[ 'ant', 'bat', 'cat', 42 ]
> a.join();                      // Join on default (comma).
'ant,bat,cat,42'
> a.join(",  ");                 // Join on comma-space.
'ant, bat, cat, 42'
> a.join(" -- ");                // Join on double dashes.
'ant -- bat -- cat -- 42'
> a.join("");                    // Join on empty space.
'antbatcat42'

Note that 42, which is an integer, is automatically converted to a string in the join.

3.4.4 Exercises

  1. The split and join methods are almost inverse operations, but not quite. In particular, confirm using == (not ===) that a.join(" ").split(" ") in Listing 3.2 is not the same as a. Why not?

  2. Using the array documentation, figure out how to push onto or pop off the front of an array. Hint: The names aren’t intuitive at all, so you might have to work a bit.

3.5 Array Iteration

One of the most common tasks with arrays is iterating through their elements and performing an operation with each one. This might sound familiar, since we solved the exact same problem with strings in Section 2.6, and indeed the solution is virtually the same. All we need to do is adapt the for loop from Listing 2.18 to arrays.

We could get there in one step fairly easily, but the connection is even clearer if we first rewrite the string for loop using the bracket access notation, which (as we saw in Section 3.2) works on strings as well. The result for the soliloquy string defined in Listing 2.15 is shown in Listing 3.3.

Listing 3.3: Combining string access and a for loop.

> for (let i = 0; i < soliloquy.length; i++) {
  console.log(soliloquy[i]);
}
T
o

b
e
.
.
.
t
i
o
n
:

The result in Listing 3.3 is exactly the same as that shown in Listing 2.18.

The application of this pattern to arrays should now be clear. All we need to do is replace soliloquy with a, as shown in Listing 3.4; the rest of the code is identical.

Listing 3.4: Combining array access and a for loop.

> for (let i = 0; i < a.length; i++) {
    console.log(a[i]);
  }
ant
bat
cat
42

One thing worth noting here is that the iteration index variable i appears in both for loops. As you may recall if you completed the exercises in Section 2.2.2, redefining a variable that’s already been declared with let generally results in an error. Why were we able to reuse i in this context?

The answer is that in the context of a for loop the scope of the variable is restricted to the loop, and disappears when the loop is finished.

That’s convenient, but it’s not the best way to iterate through arrays, and Mike Vanier still wouldn’t be happy (Figure 3.1). We’ll see a cleaner method for iterating through arrays in Section 5.4, and a way of avoiding iteration entirely in Chapter 6.

images

Figure 3.1: Mike Vanier is still annoyed by typing out for loops.

3.5.1 Exercises

  1. Show that the identifier i is undefined both before and after a for loop executes. (You might have to exit and re-enter the Node console.)

  2. Define an accumulator variable total and combine it with a loop to add all the elements of Listing 3.4. You can use the code in Listing 3.5 to get started (just replace the comment with the proper code). How does the value of total compare to a.join("")?

Listing 3.5: Skeleton for calculating a total.

> let total = "";
> for (let i = 0; i < a.length; i++) {
    // set total equal to the running total plus the current element
  }
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.242.118