The Ruby Workshop
上QQ阅读APP看书,第一时间看更新

Arrays

An array is a data structure that contains a list of values called elements. Arrays can be an ordered, integer-indexed collection of any object. Ruby arrays can have objects such as Integer, String, Symbol, Fixnum, and Hash, or even other arrays, as elements. In many languages, arrays are rigid in terms of their size. In Ruby, however, an array increases its size as elements are added.

When we say ordered, we mean that the array will keep track of how the elements are inserted or removed from it. While fetching the data from an array, we can retrieve the values directly based on the position of the element. This will become clearer as we dive into the depths of data extraction and manipulation using arrays.

Integer-indexed means that each element in the array is linked to an index. In Ruby, we can fetch the data from an array based on the index irrespective of the value at that location. Like most other languages, array indexes in Ruby start from zero.

Ruby is very flexible and provides us with many ways in which to create arrays. Let's now look at a few examples in relation to arrays.

Note

We will be using Interactive Ruby Shell (IRB) extensively in this chapter for all examples, exercises, and activities.

Beginning with arrays, here is the code for creating an empty array:

my_array = []

The output will appear as follows:

Figure 2.1: Output for an empty array

To create an array with predefined values, we need to include the values in the parentheses [], as shown in the following code snippet:

my_array = [1,2,3]

The output will appear as follows:

Figure 2.2: Output for an array with predefined values

To create an array with default values using a different method that involves the .new function in the Array class with arguments, consider the following command:

my_array = Array.new([1,2,3])

my_array = Array.new(3, "ruby")

The output on IRB would appear as follows:

Figure 2.3: Output with Array.new

The .new function is a constructor for the Array class, which can take different arguments based on your current requirements.

If we want to create an array with the first few values as empty and start from a particular index in certain scenarios, we could do so as follows:

my_array = []

my_array[4] = "ruby"

my_array

The output will be as shown in the following code block:

Figure 2.4: Output for an array with the first four values empty

Unlike languages such as Java/C, an array in Ruby can contain more than one type of element, as depicted in the following code:

my_array = ["ruby", 1, "is", 2, "awesome", 3]

The output will appear as follows :

Figure 2.5: Output for arrays with different types of elements

Iterating Through an Array

Iterating through an array can either be done with or without the index. The code for both scenarios is presented as follows:

  • Code for iterating without an index:

    my_array = [1,2,3,4,5]

    my_array.each do |element|

    puts element

    end

    The output will be as follows:

Figure 2.6: Iterating without an index

  • Code for iterating with an index:

    my_array = ["a","b","c","d","e"]

    my_array.each_with_index do |element, index|

         puts "#{element} => #{index}"

    end

    The output will be as follows:

Figure 2.7: Iterating with an index

As mentioned previously, the index starts from 0. However, note that the index is just a variable and we can play around with it just like any other variable and it will not affect how we iterate over an array. By way of a demonstration, we can create a loop over the each_with_index function of an array and show the index+10 value for the array item. The each_with_index function will iterate over each element and create a temporary variable that shows the actual index of the element in the array, before returning a pair of elements (element, index) for each iteration.

In the first iteration, this will be ('a',10). It prints out "index + 10 => element", meaning it will print "10 => a" in the first iteration. As depicted in the following code:

my_array = ["a","b","c","d","e"]

my_array.each_with_index do |element, index|

puts "#{element} => #{index + 10}"

end

The output will appear as follows:

Figure 2.8: Output for the each_with_index function

Note

Changing the value of the index variable is only valid during its current iteration and does not affect subsequent iterations of the loop.

Ruby is purely an object-oriented language. This means that arrays are objects themselves. The full Object Oriented Programming (OOP) paradigm is valid for Ruby constructs throughout the application. We can use inheritance, polymorphism, and encapsulation in our application.

Now, let's check the base class for our Array. The base class is a class from which the specific properties of a data type and methods are inherited in a new instance as a way of reusing code and maintaining functionality.

Use the following code to check the class of the array:

my_array.class

The output on IRB would appear as follows:

Figure 2.9: Output for the array class

Ruby provides us with inbuilt methods related to a specific data type that allow us to discover the inherited methods, as well as the methods of our instance. This can be achieved using the .methods function as follows:

my_array.methods

The output will be as follows:

Figure 2.10: Methods for an array

As can be seen from the preceding output, .methods will return all the method names the object can be called upon. The preceding screenshot shows 169 methods, but we'll dig deep into just a few of the most common and important methods.

Ruby allows us to check whether a method is implemented in the instance we created, and thus inherited from its base class. This can be checked on an array using the .respond_to function. Consider the following example:

my_array = ["I", "love", "ruby"]

my_array.respond_to?("length")

my_array.respond_to?("garbage")

The output will appear as follows:

Figure 2.11: Output using the .respond_to? function on an array

In the preceding example, we define a set of variables in an array and check how the array responds to the methods called on the array. If you see the output, Ruby returns true when the length method is called on the array and false when a non-existent garbage method is called on the array. This clearly means that the array will only respond to the defined methods and return false for anything other than that.

Let's now move on to implementing some operations on arrays.

Operations on Arrays

Arrays are the Swiss army knife of every programming language. They are usually used to store values or different types, to calculate intermediate or final results, and even to send data to other functions or classes. This section shows you how we can harness the power of arrays for this purpose.

In this section, we are going to study how to perform various operations on arrays.

Merging Two Arrays

Different values held in arrays can be merged, as shown in the following code:

my_array_first_half = [1,2,3]

my_array_second_half = [4, 5, 6]

my_array_first_half + my_array_second_half

The output will appear as follows:

Figure 2.12: Merging two arrays

From the preceding code, you can clearly discern that the two arrays have been merged together using the + operator.

Note that even different values, belonging to different data types, that are held in arrays can be merged.

Removing Occurrences of Similar Elements from an Array

Values that have multiple instances in the array can be removed, as shown in the following code:

my_array_with_duplicates = [1, 1, 4, 5, 6]

my_array_to_be_removed = [1,2,3]

my_array_with_duplicates - my_array_to_be_removed

The output will be as shown in the following code block:

Figure 2.13: Removing similar elements from the array

In the preceding code, we defined two arrays with a few variables. Using the operator, all the occurrences of the second array were removed from the resulting array.

If you notice, all the occurrences of 1 are removed from the result.

Inserting Elements into an Array at the End

There are many ways in which you can insert elements in an array. The following code shows how to insert elements using the push method. This will extend the current array with the new elements specified as arguments to the push function. They can be of any type; it is not necessary to have the same type of elements as the values already in the array:

my_array  = [1,2,3]

my_array.push(4)

The output will be as shown in the following code block:

Figure 2.14: Inserting elements into an array using the push method

Here, we have defined an array and pushed the variable 4 into the array using the .push method.

Another way to add elements to an array is by means of the << operator. There are operators in Ruby that are aliases to functions or methods, and they are used to achieve the same result, but with shortcuts:

my_array  = [1,2,3]

my_array  << 4

The output will be as shown in the following code block:

Figure 2.15: Inserting elements into an array using the << operator

As you can see, the variable 4 is added to the array using the << operator.

Finding the Last Element of an Array without Modifying It

You can find the last element of the array as shown in the following code:

my_array  = [1,2,3]

my_array.last

my_array

The output will be as shown in the following code block:

Figure 2.16: Finding the last element of the array

As shown in the preceding diagram, the .last method displays the last element of the array.

Finding the Last Element of an Array and Removing it

You can find the last element of the array and eliminate it, as shown in the following code:

my_array  = [1,2,3]

my_array.pop

my_array

The output will be as shown in the following code block:

Figure 2.17: Eliminating the last element from an array

The .pop method has picked up the last element and removed it from the array.

Let's now solve an exercise to deepen our understanding of operations performed on arrays.

Exercise 2.01: Performing Simple Operations on Arrays

In this exercise, we are going to execute the following operations on an array:

  • Merge arrays.
  • Remove the repeated occurrences of elements in two arrays.
  • Insert new elements into an array.
  • Find the last element of the array without modifying it.
  • Find the last element of the array and remove it.

We will define two arrays, colors_1 and colors_2, in which we will store colors of the visible light spectrum as elements, and then perform the aforementioned operations on these arrays.

The following steps will help you to complete the exercise:

  1. Go to the Terminal and enter irb for Interactive Ruby Shell.
  2. Define the two arrays that you wish to merge and merge them into one array, to be termed as colors_3:

    colors_1 = ['violet', 'indigo', 'blue', 'green']

    colors_2 = ['yellow', 'violet', 'orange', 'red', 'violet']

    colors_3 = colors_1 + colors_2

    The output of the colors_3 array would be as follows:

    Figure 2.18: Output of the colors_3 array

    As can be seen in the output, the new array includes elements from both the colors_1 and colors_2 arrays. However, as you might notice, some colors are repeated. We will eliminate the duplicates in the next step.

  3. Next, use the - operator to remove the repeated occurrences of the elements in the two arrays:

    colors_repeated = ['violet']

    colors_new = colors_3 - colors_repeated

    The output of the colors_new array would be as follows:

    Figure 2.19: Output of the colors_new array

    You can see that the array only has unique elements. Now, let's try inserting new elements into our arrays.

  4. Then, insert new elements into the array:

    colors_1.push('yellow')

    colors_1.push('red')

    colors_2.push('green')

    colors_2.push('blue')

    The new colors_1 and colors_2 arrays will be as follows:

    Figure 2.20: Output of the colors_1 and colors_2 arrays

    You can now see how the yellow, red elements have been added to the colors_1 array, and how the green, blue elements have been added to the colors_2 array.

  5. Now, we will trace the last element of the array without modifying the array:

    colors_1 = ['violet', 'indigo', 'blue', 'green']

    colors_1.last

    The output will be as follows:

    Figure 2.21: Last element of the array

    As you can see from the output, the .last method has picked up the last element of the array.

  6. Next, we will find the last element in the array and remove it:

    colors_1 = ['violet', 'indigo', 'blue', 'green']

    colors_1.pop

    colors_1

    The output should be as follows:

Figure 2.22: Output of the colors_2 array without the last element

As you can see from the output, the .pop method has removed the last element from the array.

Thus, we have successfully performed basic operations on arrays.

Until now, we have only performed basic operations on arrays. However, there are many more operations, such as "freezing" an array, finding unique elements, and sorting elements.

Let's now look at each of these in more detail.

Creating an Array That Cannot Be Modified

This can come in handy when we want to limit the size of an array or prevent further modification. The freeze function can be used on objects other than arrays with the same result:

my_array  = [1,2,3]

my_array.freeze

my_array << 4

The output will be as shown in the following code block:

Figure 2.23: FrozenError

As we can see, the error in the output clearly implies that a frozen array cannot be modified.

Finding All the Unique Elements in an Array

uniq means we want to have every occurrence of an element only once in our array. We can retrieve a different object with the results or just modify our original list in place.

Consider the following example:

my_array  = [1,2,3, 4,3,2]

unique_array = my_array.uniq

my_array

The output will be as shown in the following code block:

Figure 2.24: Output of the uniq function

Note that in the preceding case, we do not modify the existing array, rather a new array is returned by the uniq method. If we want to modify the existing array, we can use ! as follows:

my_array  = [1,2,3, 4,3,2]

my_array.uniq!

my_array

The output will be as shown in the following code block:

Figure 2.25: Output for the uniq! function

As you can see, uniq! has modified the array, keeping only one instance of each element.

Sorting the Elements of an Array

Remember that if we have to modify the existing array, we need to use !, as in sort!. We can also reverse sort an array. Sorting comes in handy when we do not have any control over the order in which the array elements arrive, but we want to process them in some kind of order. .reverse will give us the reverse order of the elements after sorting them, so it's two steps behind the scenes:

my_array = [3,6,23, 1]

my_array.sort

my_array.sort.reverse

The output will be as shown in the following code block:

Figure 2.26: Sorting elements

As you see in the preceding output, the array has been arranged in ascending order and also reversed using .reverse.

Finding the Number of Elements in an Array

The .length and .size methods do exactly the same thing and we can use either of them. Consider the following example:

my_array  = [1,2,3]

my_array.length

my_array.size

The output will be as shown in the following code block:

Figure 2.27: Output for the length and size methods

As you can see, the .length and .size methods have returned the size and length of the array.

Note

In Ruby convention, any method that ends with ? implies that it will return a Boolean value. While writing our own methods, we should observe the same practice.

Establishing Whether an Element Exists in an Array (Case-Sensitive)

include? is used to find the occurrences of an element in an array. It is case-sensitive and is an extremely useful method when it comes to large arrays.

Consider the following example where we will call the include? method on two variables with different cases:

my_array = ["I", "love", "ruby"]

my_array.include?("ruby")

my_array.include?("Ruby")

The output will be as shown in the following code block:

Figure 2.28: Output of the include? method

As you can see, include? has identified the element mentioned in the array and stated the output accordingly as per the element that is being searched for. The array contains ruby and, hence, the method has returned true for ruby, and false for Ruby.

Converting Elements of an Array into a String

The purpose of join here is to combine each element of the array with the string that is passed as a parameter to the join method.

Consider the following example:

my_array = ["I", "love", "ruby"]

my_array.join(",")

my_array.join(" ")

The output will be as shown in the following code block:

Figure 2.29: Output of the join method

As we can see, the element , has been added to the variables in the array in the first case, and a whitespace has been added, in the second case. join has successfully combined the elements with a string passed as a parameter.

Exercise 2.02: Performing Complex Operations on Arrays

In this exercise, we will implement all the array operations discussed in the preceding section. We will define an array having numbers as elements for this purpose and then proceed to perform the various array operations.

The following steps will help you to complete the exercise:

  1. Go to the Terminal and enter irb for Interactive Ruby Shell.
  2. Define the array and use .freeze to make it unchangeable. Make sure you use a different notation for the new array so that you can use the original array in subsequent steps:

    my_array = [1,2,3,4,5,6,6,4,3,2,1]

    my_array.freeze

    The output will appear as shown in the following code block:

    Figure 2.30: Output of the frozen array

    Now that we have frozen the array, it is not possible to make any changes to this array or perform any operations on it.

  3. You may have observed that there are a few values that are repeated in our array. Now, let's write the code to list the unique elements in the array using .uniq:

    my_uniq_arry = my_array.uniq

    The array should now be as shown in the following code block:

    Figure 2.31: Output for .uniq

    You can see that the new array has eliminated the repeat occurrences of the variables and returned only the unique elements of the array.

  4. Next, we use .sort to arrange the elements in alphabetical order:

    my_sorted_array = my_array.sort

    The output for the .sort method will be as follows:

    Figure 2.32: Output for the .sort method

    As can be seen in the preceding figure, the array has now sorted the elements in ascending order.

  5. Now, we find the total number of elements in the array using .length and .size:

    my_array.size

    my_array.length

    The array size and length are as shown in the following code block:

    Figure 2.33: Output for the size and length of the array

    With .length and .size, we have derived the actual length and size of the array.

  6. Next, we shall use .include to establish whether a particular element exists in the array:

    my_array.include?(1)

    my_array.include?(11)

    The output will be as shown in the following code block:

    Figure 2.34: Output for the .include method

    As you can see, the .include? method returns true when checked for the element 1, and false for the element 11.

  7. Lastly, we shall be converting the elements of the array into a string:

    my_array.inspect

    The output will be as shown in the following code block:

Figure 2.35: Output for .inspect

With the completion this exercise, we have successfully implemented all the aforementioned methods on an array and established a basic understanding of arrays in Ruby.