Ruby Arrays - Intermediate


You already know what an array is, an ordered collection of elements. Now you need to learn how to work with arrays. In this chapter you'll learn the different ways arrays can be created along with how to add, remove, and sort elements. You'll also learn some advanced features of Ruby along the way. By the end of this chapter, you will have mastered the Array.

Ruby arrays are unique in that they can contain objects of different types. Several other languages require all elements in an array to be of the same type. So, for example, you can have an array of numbers or an array of words but you can't have an array of numbers and words. Ruby does not have such a restriction. You can mix different data types within a single array:

>> array = ["dot", 4, "elite", "smaller", 9, false, {"first_name": "bob"}]
[
    [0] "dot",
    [1] 4,
    [2] "elite",
    [3] "smaller",
    [4] 9,
    [5] false,
    [6] {
        :first_name => "bob"
    }
]

Here we have an array named, 'array', containing four different data types. There are strings, integers, a hash, and a boolean value. We can also have an array inside of our array. Let's add an array to our array using the '<<' method.

>> array << [1, 2, 3, 4, 5]
[
    [0] "dot",
    [1] 4,
    [2] "elite",
    [3] "smaller",
    [4] 9,
    [5] false,
    [6] {
        :first_name => "bob"
    },
    [7] [
        [0] 1,
        [1] 2,
        [2] 3,
        [3] 4,
        [4] 5
    ]
]

We now have an array with 7 elements, where the 7th element is itself an array. The << method allows us to append an element without overwriting any of the existing elements. There are now 5 different data types in our array. This is an example of the versatility of Ruby. There may not be many use cases for storing numbers, strings, booleans, etc in a single array. However, you'll often store arrays or hashes in an array but more on that later.

Creating arrays

Ruby provides multiple ways to create arrays. We have been creating arrays with what's called an 'array literal'. That simply means we are specifying a variable and literally putting an array inside of it. We've been doing that with square brackets [ ]. There is also a 'new' constructor that can be used with the Array class:

>> fancy = Array.new
[]
# we've created a new array with no data. It's empty

Here we assign the variable 'fancy' to a new array. Ruby always returns an object. In this case, our new array is returned to us. We can create an empty array in the same way by specifying empty brackets. For example:

>> fancy = []
[]
#same as the previous example

You may remember the shortcut for creating an array of stings:

>> shortcut = %w(i am a new array)
[
    [0] "i",
    [1] "am",
    [2] "a",
    [3] "new",
    [4] "array"
]

This gives us an array of strings. We could include a number but it would be converted into a string.

>> shortcut = %w(i am a new array 9)
[
    [0] "i",
    [1] "am",
    [2] "a",
    [3] "new",
    [4] "array",
    [5] "9"
]

The number nine is included in our array but as you can tell by the quotations it was included as a string. This is an effect of %w. Also, when you are using %w, you can encase the array with any special character. You are not limited to parentheses ( ) or square brackets [ ]. For example:

# using $
>> %w$ i am an array$
[
    [0] "i",
    [1] "am",
    [2] "an",
    [3] "array"
]
# using *
>> %w* i am array*
[
    [0] "i",
    [1] "am",
    [2] "array"
]

As long as the final character and starting character are the same you can use any special character. The interpreter just needs to know your array starts and ends. This isn't a particularly useful tidbit of information but sometimes it's fun to switch it up a bit.

Finally, the last way to create an array is by using the .to_a method (the 'a' obviously stands for array). This makes it especially easy to convert a range to an array:

>> (1...10).to_a
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6,
    [6] 7,
    [7] 8,
    [8] 9
]

Retrieving Data

You now know the many different ways ruby allows you to create arrays. Let's learn how to retrieve data from our arrays. You can easily retrieve the data for any element if you know its position in the index. Let's create an array of names to use as an example. Here are the gold, silver, and bronze medalists of the 2016 Olympic badminton games ( in that order).

>> rio = ["Chen Long", "Lee Chong Wei", "Viktor Axelsen"]
[
    [0] "Chen Long",
    [1] "Lee Chong Wei",
    [2] "Viktor Axelsen"
]

Recall that Ruby like most programming languages uses zero-indexed arrays. Therefore, to retrieve the gold medalist for badminton in 2016 we type the name of the array and put the index in square brackets [ ].

>> rio[0]
"Chen Long"

It's that simple! We asked for the data in array "rio" at position 0 and ruby responds accordingly. We can ask for the next element by referencing index 1, or we can ask for all elements by referencing a range:

>> rio[0..2]
[
    [0] "Chen Long",
    [1] "Lee Chong Wei",
    [2] "Viktor Axelsen"
]

Awesome! Ruby returned a range of elements just like we wanted. There is also a method for retrieving the data in the array, .at(). By calling .at(#) and specifying the index, ruby will return that element. Since 'at' is a method be sure to use parenthesis around the index. Calling the index directly or using 'at' are two ways to accomplish the same thing.

>> rio.at(0)
"Chen Long"

So far we have been retrieving data from indices we know exist. If you were to call an index or use 'at' for an index that does not exist, ruby will return nil. There is yet another method that fetches data from an array, aptly named 'fetch'. This method is similar to 'at' with the added benefit of being able to specify a default response for calls that are outside of the array bounds. We can use "fetch" either way. To retrieve data like we have been, or to specify a default "out of bounds" response.

# normally retrieve data
>> rio.fetch(1)
"Lee Chong Wei"

# specify a default response if range is outside of bounds
>> rio.fetch(5, "oh gosh, something went wrong")
"oh gosh, something went wrong"

So, what are the "bounds" of an array?

This is a great question! We can find the answer by exploring a common scenario. There will be times when you need to get the first element of an array. You already know you can use array[0], or in continuing with our example, rio[0]. There is also an alternative way of doing this, the .firstmethod. This is personally my favorite option because I believe its the simplest. To show you this in IRB:

>> rio.first
"Chen Long"

However, what if you wanted to get the last item in the array and you didn't know how many elements there were? Based on what you just learned you may infer that you can use the .last method. This is correct, it will, in fact, provide the last element in the array. But what about if you needed the second to last item and you don't know how many elements there are? Now, we have a predicament. This is where the bounds of the array derive from. You can retrieve the last item of the array by using index -1. So, sticking with our Olympic games example:

>> rio[-1]
"Viktor Axelsen"
>> rio.last
"Viktor Axelsen"

No matter how many items there may be, the last item will be at index -1. Then we can also access the second to last item with index -2. The third to last with index -3, and so on. Therefore, the array bounds are all of the possible indices. In our example, it would be the range (-3...3). Remember three dots indicates the range does not include the final value. We can call index 0, 1, and 2 moving forward along the number line. We can also call index -1, -2, and -3 moving backward along the number line.

Another method, .take, returns the first n elements of the array. We can return the first two in our example with:

>> rio.take 2
[
    [0] "Chen Long",
    [1] "Lee Chong Wei"
]
# parentheses are optional

Remember that parentheses are optional when calling methods. The general rule of thumb is to leave them off if you aren't passing additional arguments. So, instead of calling rio.first()use rio.first. You don't need to use empty parenthesis to show a function call, that's what JavaScripters do. In the previous example, I would normally use them since I'm passing in "2" but I wanted to leave them off to show you that it does still work.

The 'take' method has an inverse, .drop, which returns the remaining elements after n elements have been dropped.

>> rio.drop(1)
[
    [0] "Lee Chong Wei",
    [1] "Viktor Axelsen"
]

Our gold medalist, Chen Long, was dropped from the array and the results were returned to us.

Counting elements in an array

There are multiple ways to check the number of elements in an array. The Ruby documentation states, "To query an array about the number of elements it contains, use length, count, or size". Let's test out these methods.

>> rio.length
3
>> rio.count
3
>> rio.size
3
# it's common to find more than one way of doing things in ruby

Check if an array is empty with the .empty? method.

>> rio.empty?
false

Check for a particular value with the .include? method.

>> rio.include?("Lin Dan")
false
# Lin Dan won the gold in Beijing in 2008 and again in London in 2012

Adding elements to an array

You've seen how to append an item to the end of an array with << . You can also use the .push method to push an item into an array.

# create an array with 5 numbers
>> numbers = [1, 2, 3, 4, 5]
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5
]
# add number 6 to the array
>> numbers << 6
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6
]
#add number 7 to the array
>> numbers.push(7)
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6,
    [6] 7
]

At this point, we have an array, referenced with the variable 'numbers'. Then we realize that in our array we forgot to start with 0. We'll need a way to add the number 0 but we don't want to add it to the end of the array we want to add it to the beginning of the array.

>> numbers.unshift(0)
[
    [0] 0,
    [1] 1,
    [2] 2,
    [3] 3,
    [4] 4,
    [5] 5,
    [6] 6,
    [7] 7
]

The .unshift method is the perfect solution for our problem. We use 'unshift' to add an element at the start. But what if we want to add something at a specific position? For example, let's say we want to add the number 25 at the 5th position. We can use the .insert method for this, passing in the index position as the first argument and the element as the second argument.

>> numbers.insert(5, 25)
[
    [0] 0,
    [1] 1,
    [2] 2,
    [3] 3,
    [4] 4,
    [5] 25,
    [6] 5,
    [7] 6,
    [8] 7
]

Here we are specifically telling ruby, "for the array numbers, I want you to insert at position 5, the number 25". Then ruby responds with, "Ok. Here you are" and shows us our new array. One benefit of using insert is that you can specify multiple elements at once. The first argument is still the position in the array, each argument after that will be an element added to the array. Let's add a bunch of numbers near the middle of our array:

>> numbers.insert(4, 88, 77, 66, 1, 99)
[
    [ 0] 0,
    [ 1] 1,
    [ 2] 2,
    [ 3] 3,
    [ 4] 88,
    [ 5] 77,
    [ 6] 66,
    [ 7] 1,
    [ 8] 99,
    [ 9] 4,
    [10] 25,
    [11] 5,
    [12] 6,
    [13] 7
]

Removing elements from an array

There are multiple methods for removing items from an array. One such method, .pop, removes the last item in the array and returns the removed item. Think of this as ruby's way of showing you what element that was deleted. This is a destructive method as it alters the original array. Here is an example from IRB:

>> awesome_array = [1, 2, 3, 4]
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4
]
>> awesome_array.pop
4
>> awesome_array
[
    [0] 1,
    [1] 2,
    [2] 3
]

The .pop method "popped" the last element, permanently erasing it from 'awesome_array'. There is a similar method for removing the first item in an array called, .shift. This method behaves in the same way, by returning the deleted item. Continuing with our array from the previous example:

>> awesome_array.shift
1
>> awesome_array
[
    [0] 2,
    [1] 3
]
>>

More often than not you'll want to delete a particular item or an item at a particular index. Ruby provides the .delete and .delete_at methods to accomplish each task respectively. Our awesome array only contains two items, numbers 2 and 3. We can delete the number two by passing it as an argument to the delete method. Ruby returns the deleted item in the same way it previously has.

>> awesome_array.delete(2)
2 
>> awesome_array
[
    [0] 3
]

Our awesome array now consists of a single lonely number. We can remove this last item by specifying the index we wish to delete:

>> awesome_array.delete_at(0)
3
>> awesome_array
[]

Another commonly used method to delete items by index is .slice. The 'slice' method is non-destructive and returns the item at the specified index. The method can become destructive by adding an exclamation point, slice!.

# first we create an array of 10 numbers
>> numbers = (1..10).to_a
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6,
    [6] 7,
    [7] 8,
    [8] 9,
    [9] 10
]
# the slice method takes an index and returns the corresponding item
>> numbers.slice(1)
2
# notice how the array remains unchanged. slice is non-destructive
>> numbers
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6,
    [6] 7,
    [7] 8,
    [8] 9,
    [9] 10
]
# slice can also take a range of indices
# Note that slice! is used here
>> numbers.slice!(1..2)
[
    [0] 2,
    [1] 3
]
# slice! is the destructive version of slice. the numbers 2 and 3 have been removed
>> numbers
[
    [0] 1,
    [1] 4,
    [2] 5,
    [3] 6,
    [4] 7,
    [5] 8,
    [6] 9,
    [7] 10
]

There are two other useful methods for deleting items in an array, compact and uniq. Each has a special use case. The first, .compact, removes nil values from an array. The method is non-destructive. It returns the array with the nil values removed but does not alter the original array. There is a destructive version, .compact!, which removes the nil values and alters the original array. The second method, uniq, removes duplicate values in an array. It is also non-destructive like compact but has a destructive version, uniq!.

The exclamation point ( ! ), also called bang, is frequently used throughout ruby. In most cases, it turns a non-destructive method into a destructive method.

Iterating over Arrays

Once you've mastered the enumerable mix-in you'll find that iterating over collections is actually quite simple. First, let's quickly create an array of numbers like so:

>> numbers = (1..10).to_a
[
    [0] 1,
    [1] 2,
    [2] 3,
    [3] 4,
    [4] 5,
    [5] 6,
    [6] 7,
    [7] 8,
    [8] 9,
    [9] 10
]

If we want to select the even numbers from our array we can use the .select method from the previous chapter.

>> numbers.select {|n| n % 2 == 0}
[
    [0] 2,
    [1] 4,
    [2] 6,
    [3] 8,
    [4] 10
]

Take a moment to appreciate the simplicity of ruby's syntax. We are asking ruby, "take our array named numbers and select the numbers which are evenly divisible by 2". Ruby reads closer to spoken English than any other computer language.


Quick summary

In this chapter, we covered the Ruby Array at an intermediate level. The array is a data structure containing an ordered list of objects.

Create arrays with

  • Array.new
  • some_variable = [ ]
  • .to_a method

View elements with the syntax array[index] or array.at(index)

Add elements to arrays with

  • .push - adds element to the end of an array
  • << - adds element to the end of an array
  • .unshift - adds element to the beginning of an array
  • .insert(index, element) - add element to specified index

Remove elements from an array with

  • .pop - deletes the last item in an array
  • .shift- deletes the first item in an array
  • .delete(element) - deletes a specific element
  • .slice!(index) - deletes element at specific index
  • .delete_at(index) - deletes element at specific index

results matching ""

    No results matching ""