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 Ruby container for a list of 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 other 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.
Perhaps the most common use of split
is with no arguments; in this case, the default behavior is to split on whitespace (such as spaces, tabs, or newlines):
>> "ant bat cat".split
=> ["ant", "bat", "cat"]
>> "ant bat\t\tcat\n duck".split
=> ["ant", "bat", "cat", "duck"]
We’ll investigate this case more closely when discussing regular expressions in Section 4.3.
3.1.1 Exercises
- 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? - 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
:
>> a = "badger".split("")
=> ["b", "a", "d", "g", "e", "r"]
We can access particular elements of a
using the same bracket notation we first encountered in Section 2.6, as seen in Listing 3.1.
>> a[0]
=> "b"
>> a[1]
=> "a"
>> a[2]
=> "d"
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”.1
So far we’ve dealt exclusively with arrays of characters, but Ruby arrays can contain all kinds of elements:
>> soliloquy = "To be, or not to be, that is the question:"
>> a = ["badger", 42, soliloquy.include?("To be")]
=> ["badger", 42, true]
>> a[2]
=> true
>> a[3]
=> nil
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 the defined range returns nil
(a value which we saw before in the context of puts
(Listing 1.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 Ruby is more tolerant in this regard.
Another convenient feature of Ruby bracket notation is supporting negative indices, which count from the end of the array:
>> a[-2]
=> 42
Among other things, negative indices give us a compact way to select the last element in an array. Because arrays, like strings, respond to a length
method, we could do it directly:
>> a[a.length - 1]
=> true
But it’s even easier like this:
>> a[-1]
=> true
This is such a common operation that Ruby supplies a special last
method just for doing it:
>> a.last
=> true
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.2.1 Exercises
- Write a
for
loop to print out the characters obtained from splitting “honey badger” on the empty string. - See if you can guess the value of
a[100]
in a boolean context. Use!!
to confirm.
3.3 Array slicing
In addition to supporting the bracket notation described in Section 3.2, Ruby 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]
One way to slice an array is to provide two arguments, an index and a number of elements, which returns the given number of elements starting at the given index. For example, for an array with four elements, slice(2, 2)
returns the “second” and “third” ones (recall that the “first” or zeroth element has index 0
):
>> a.slice(2, 2)
=> [17, 99]
Another technique (and my favored one) is to use the Range
data type we met briefly in Listing 2.18, which returns the elements with index between the beginning and end of the range:
>> a.slice(1..3)
=> [8, 17, 99]
Unlike most languages, Ruby lets us perform array slicing directly with the bracket notation:
>> a[2, 2]
=> [17, 99]
>> a[1..3]
=> [8, 17, 99]
The second example above, using a range, is probably the most common way to slice arrays in real-world Ruby code.
Finally, it’s worth noting that ranges can easily be converted to arrays using the “to array” method, to_a
:
>> (1..10).to_a
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>> ('a'..'z').to_a
=> ["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"]
We see in the second example above that ranges work on letters as well as numbers.
3.3.1 Exercises
- 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. - Show that strings also support the
slice
method by selecting justbat
from the string"ant bat cat"
. (You might have to experiment a little to get the indices just right.) - By combining a range,
to_a
, and array slicing, create an array containing the first 13 letters in the alphabet.
3.4 More array methods
In addition to last
, length
, and slice
, arrays respond to a wealth of other methods. As usual, the documentation is a good place to go for details.
As with strings, arrays respond to an include?
method to test for element inclusion:
>> a = [42, 8, 17, 99]
=> [42, 8, 17, 99]
>> a.include?(42) # Test for element inclusion.
=> true
>> a.include?("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.2 In Ruby, we just call sort
:
>> a.sort
=> [8, 17, 42, 99]
>> a # `a` hasn't changed as the result of `sort`.
=> [42, 8, 17, 99]
As you might expect for an array of integers, a.sort
sorts the array numerically (unlike, e.g., JavaScript, which confusingly sorts them “alphabetically”, so that 17 comes before 8). We also see that (again unlike JavaScript) sorting an array doesn’t change the array itself.
Ruby includes a second sort method that does change the array, sorting it in place:
>> a.sort!
=> [8, 17, 42, 99]
>> a # `a` has changed as the result of `sort!`.
=> [8, 17, 42, 99]
Here the sort!
(read “sort-bang”) method makes use of Ruby’s ability to include punctuation in method names, as we saw in Section 2.5 with boolean methods like include?
. In this case, the exclamation point indicates that the method mutates (changes) the underlying object. Any time you see a Ruby method ending in !
, it’s more than likely that some sort of mutation is occurring.3
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, 42, 17, 8]
>> a # Like `sort`, `reverse` doesn't mutate the array.
=> [8, 17, 42, 99]
As with sort
, reverse
returns a reversed array, but doesn’t change the array itself. Can you guess the method for mutating an array by reversing it in place?
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
=> [8, 17, 42, 99, 6]
>> a.push("foo")
=> [8, 17, 42, 99, 6, "foo"]
>> a.pop # `pop` returns the value itself
=> "foo"
>> a.pop
=> 6
>> a.pop
=> 99
>> a
=> [8, 17, 42]
As noted in the comments, pop
returns the value of the final element (while removing it as a side effect), while push
simply returns the resulting array.
We are now in a position to appreciate the comment made in Section 3.2 about obtaining the last element of the array, as long as we don’t mind mutating it:
>> the_answer_to_life_the_universe_and_everything = a.pop
=> 42
Finally, Ruby supports a second (and very commonly used) notation for pushing onto arrays, the so-called “shovel operator” <<
:
>> a << "badger"
=> [8, 17, "badger"]
>> a << "ant" << "bat" << "cat"
=> [8, 17, "badger", "ant", "bat", "cat"]
As seen in the second example, the shovel operator can be chained, pushing a sequence of elements onto the end of the array.
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).
>> a = ["ant", "bat", "cat", 42]
=> ["ant", "bat", "cat", 42]
>> a.join # Join on default (empty space).
=> "antbatcat42"
>> a.join(", ") # Join on comma-space.
=> "ant, bat, cat, 42"
>> a.join(" -- ") # Join on double dashes.
=> "ant -- bat -- cat -- 42"
Note that 42
, which is an integer, is automatically converted to a string in the join.
3.4.4 Exercises
- Confirm your guess about the version of
reverse
that mutates an array by reversing it in place. - The
split
andjoin
methods are almost inverse operations, but not quite. In particular, confirm using==
thata.join(" ").split(" ")
in Listing 3.2 is not the same asa
. Why not? - 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.20 to arrays, i.e., replace soliloquy
with a
, as shown in Listing 3.3.
for
loop.
>> for i in 0..(a.length - 1)
?> puts a[i]
>> end
ant
bat
cat
42
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).

for
loops.
Luckily, looping the Right Way™ is easier than it is in most other languages, so we can actually cover it here (unlike in, e.g., Learn Enough JavaScript to Be Dangerous, when we had to wait until Chapter 5). The trick is to use a special each
method that is particularly characteristic of Ruby, as shown in Listing 3.4.
each
to iterate over an array the Right Way™.
>> a.each do |element|
?> puts element
>> end
ant
bat
cat
42
The array a
responds to the each
method by yielding each element in turn, which in this case we simply print to the screen. (The syntax in Listing 3.4 uses a Ruby block, but don’t worry about it for now; we’ll cover blocks in more detail in Section 5.4.) Using the each
method, we can iterate directly through the elements in an array, thereby avoiding having to type out Mike Vanier’s bête noire, “for (i = 0; i < N; i++)”. The result is cleaner code and a happier programmer (Figure 3.2).

each
has made Mike Vanier a little happier.
3.5.1 Exercises
- Combine
reverse
andeach
to print out an array’s elements in reverse order. Hint: You can call each method in sequence, as ina.reverse.each
, a technique known as method chaining (covered in Section 5.3).