Categories
Dev Bootcamp

Ruby Classes, Instances and Inheritance

Launch Your Understanding From 0-60 with Ruby Classes

Just as with any object-oriented programming language, class is an important part of Ruby. Everything in Ruby revolves around objects, and all of these objects belong to a class. Unlike in other languages, where each object can have multiple classes, objects in Ruby can only belong to one class. A class is a structure containing similar properties that each instance in the class share, but each instance can have different characteristics for those properties. Arrays, Hashes, and Strings are all examples of classes!

I threw out a lot of terminology, so let me show you an example:

class SuperCar
  def initialize(make, model, year, horsepower, torque)
    @make = make
    @model = model
    @year = year
    @hp = horsepower
    @tq = torque
  end
  def print_specs
    puts "This #{@year} #{@make} #{@model} has #{@hp} horsepower and #{@tq} lb-ft of torque."
  end
end

car_1 = SuperCar.new('Ferrari','458 Italia',2013,570,398)
car_2 = SuperCar.new('Lamborghini','Aventador',2012,691,508)
car_3 = SuperCar.new('McLaren','MP4-12C',2014,616,443)

There are several pieces of code to look at here. We have a class called SuperCar and three instances called car_1, car_2, and car_3. To define a new class, we began by writing the keyword class and then the constant SuperCar. Note that the constant for a class MUST start with a capitalized letter.

The class SuperCar contains the general structure and functions that each instance of this car has. This means that car_1, car_2, and car_3 each has the variables @make, model, @year, @hp, and @tq; and can use the methods initialize and printer_specs. It is important to note that car_1, car_2, and car_3 are not just limited to just these two methods. There are other ways to add new methods to these objects, which are not covered in this post.

Another thing that you see here are instance variables. They are the “properties” of the class, and each instance of the class has a different set of instance variables (i.e. car_1’s @make is Ferrari while car_2’s @make is Lamborghini). Instance variables can be used anywhere within the instance of the class. In the example, the initialize method stores the 5 parameters from initializing a new SuperCar into 5 instance variable assignments. These instance variables can be used in the print_spec method, as well as any other method you decide to create from within the class.

You’d call an instance method that you’ve created in the new class with the . notation, the same way you’d call an instance method from the built-in classes, such as Integers, Arrays, and Hashes. Here is an example of an instance method call and what is displayed on the console:

car_1.print_spec
>>"This 2013 Ferrari 458 Italia has 570 horsepower and 398 lb-ft of torque."
ar_2.print_spec
>>"This 2012 Lamborghini Aventador has 691 horsepower and 508 lb-ft of torque."

While an object cannot belong to more than one class, a class can be a descendant of another class. Descendant classes inherit all of the instance methods from the ancestor class, as well as the ancestor of the ancestor class, and so on.

For example, a certain super car collector takes a portion of his super cars to the racetracks:

class TrackCar < SuperCar
  def set_laptime=(time)
    @laptime = time
  end
  def get_laptime
    @laptime
  end
end

car_4 = TrackCar.new('Ferrari','458 Italia Speciale',2014,597,398)

Again, we have several pieces of code to look at here. In the first piece, I created a class called TrackCar and used the < notation to specify that TrackCar is a descendant of the SuperCar class. This class has two methods: set_laptime (this is a setter method) and get_laptime (and this is a getter method). A setter method allows you to set an instance variable to a value and a getter method allows you to get the value of an instance variable.

Notice that the set_laptime method looks a little funky. Ruby allows you to use the = notation in instance methods to make calling it look more reader-friendly and consistent with variable assignment conventions (i.e. car_4.set_laptime = 98 as opposed to car_4.set_laptime(98)). There is another shortcut to write setter and getter methods, using attr_ attributes (to learn more about them, you can start here).

In the last piece of the code, I created an instance of TrackCar called car_4. Now, let’s run several methods:

car_4.print_specs
>>"This 2014 Ferrari 458 Italia Speciale has 597 horsepower and 398 lb-ft of torque."
car_4.set_laptime = 98
puts "This car's fastest lap time is #{car_4.return_laptime} seconds."
>>"This car's fastest lap time is 98 seconds."

Notice that the print_specs method works even though it was not written in the TrackCar class. This is because TrackCar is a descendant of SuperCar. Any instance of the class TrackCar can use methods from its ancestor classes. If you do not specify an ancestor class, it will default to the class Object. Object is a descendant of BasicObject, which is at the top of the Ruby class hierarchy. That means that all Ruby classes eventually go up to Object and then BasicObject.

This is a good place to stop. We’ve talked about classes, instances, instance variables, instance methods, and class inheritance. To summarize it all, as tutorialspoint puts it, “A class is essentially a blueprint from which individual objects are created.”

This blog has been initially published on tonymai.github.io.

Categories
Dev Bootcamp

A Tear-Down of the Enumerable map Method

What does map do?

Here is the map method:

map { |item| block }

And here is what it returns:

#=> new_array

Essentially, map is an Enumerable method that runs a code block once for each element of the object and returns a new array of of the results of the block.

If you’re just starting to learn programming, you may be asking, “what does this really mean?” Let me try to break it down.

Enumerable provides collection classes with several searching and sorting methods. Arrays and Hashes are two examples of classes that have Enumerable properties, and therefore, can use Enumerable methods. map is one of those Enumerable methods.

What does it really mean? I work out.

Here is an example senario and code:

benched_weights = [90,140,160,120,165]
benched_weights.map { |wt| wt + 45 } 
#=> [135,185,205,165,210]

One the first line of the code, you’re keeping track of how much weight you are putting onto the barbell when you bench on chest days. You are storing this in an array called benched_weights. On the first day, you put 90lbs on the bar. On the second day, you put 140lbs on the bar. Third day, 160lbs. You had a bad day on the fourth day and only put 120 lbs on the bar. And so forth…

Now, let’s say, you want to see how much weight you are benching in total, including the weight of the barbell itself. The barbell weighs 45lbs. On the second line if the code, you used the map method. You assigned the variable wt in between the pipes as a placeholder for each element in the Array and then a code block that says wt + 45. What the code is doing here is going through each weight that you logged and adding 45 to it.

During the first pass, wt is equal to the first element of the array, which is 90, and then returns wt + 45, which is 135. After the first pass, it loops to the second element, which is 140, and then returns wt + 45, which is 185. Third pass, the wt is 160 and returns wt + 45, which is 205, and so on until it goes through each element in the given array.

When all is said and done, the map method will return all of the values given by the code block in the form of a new Array, which is shown on the third line.

As a side note, you can also use Enumerable#collect interchangeably with #map. They are completely synonymous (i.e. benched_weights.collect { |wt| wt + 45 }.

How does this compare to “each”?

Many beginner’s Ruby books and courses use the each method as an introduction to teach iterators and code blocks. If you are familiar with the Array each method, you’ll notice that the Enumerable map method is very similar to it. Both methods calls the code block once for each element in the object. The key difference between the two methods stop here.

After the each method runs the block for each element, it stops and then returns nil. The map method, on the other hand, stores the results that the code block return for each element into a new array and then return the new array after the method is finished. Using the map method can save you several lines of code if you’re making the each method store the results in a new array. It can also be useful in many other ways.

If you have any questions or what further clarification, please leave a message in the comment box below.

This blog has been initially published on tonymai.github.io.

Categories
Dev Bootcamp

What Are The Differences Between Ruby Arrays And Hashes, And When To Use Each?

Overview

This is a pretty long blog, so I will seperate this post into three parts:

  1. Arrays
  2. Hashes
  3. Summary

Arrays

1a. What is an Array?

Do you find yourself assigning way too many variables to different values? Arrays are the solution to that.

A array is a list of objects (strings, numbers, booleans, another array, etc.). Every slot in an array acts like a variable.

Examples:

['Hello','World']
['My','name','is','tony']
[17.0,'male',true]

1b. How do I create an Array?

One way to create an array is to put the values you want to store in a set of brackets, like this:

my_array = ['object_1','object_2','object_3']

Another way is to use the new method:

my_array = Array.new #no arguments, creates an empty array
my_array = Array.new(3) #one argument, creates an array with the specified number of values, which will be nil. In this example, it will create an array of 3 nil values: `[nil, nil, nil]`
my_array = Array.new(3, 'object') #two arguments, creates a array with the specified number of values with the specified value. In this example, it will create: `['object', 'object', 'object']`

1c. How do I access elements in an Array?

Arrays are ordered lists that use integers as an index. Each element in an Array is stored in a specific order and can be retrieved by calling the number it is associated with by using the #[] method. One thing to keep in mind, though, is that the ordered list starts at 0. The first element in an Array is at position 0, the second element is at position 1, etc.

Examples:

my_array = ['I','am',24]
my_array[0] #returns 'I'
my_array[1] #returns 'am'
my_array[2] #returns 24

You can also do the same thing using the at method.

Example:

my_array.at(1) #returns 'am'

1d. What are some other Array methods?

Hashes

2a. What are Hashes?

A hash is an unordered list of unique keys and their values. They are similar to Arrays. One key difference is that while an Array associates its list of values with an index of integers, a Hash allows you to assign any object type to the values as a key. So a Hash stores the key and the value in the list; kind of like a dictionary.

2b. How do I create a Hash?

One way to create hash is to put the keys and values you want to store in a set of curly brackets, like this:

my_hash = { 'gender' => 'male',
'legal' => true,
'height' => 62
}

Noticed how I used => to assign a key to a value and commas to separate each pair of key and value. Each key MUST be unique. There can NOT be a duplicate key in the same hash.

Another key difference is that hashes allows an alternative syntax, called symbols, as keys (marked by : in front of the name):

my_hash = { :gender => 'male',
:legal => true,
:height => 62,
}

Or (more conveniently):

my_hash = { gender: 'male',
legal: true,
height: 62
}

A second way to add a hash is:

my_hash = Hash.new
my_hash['gender'] = 'male'
my_hash['legal'] = true
my_hash['height'] = 62

2c. How do I access elements in a Hash?

Hash elements can be accessed in a similar fashion as Array elements, but instead of calling for the integer index, you would call for the key.

Examples:

my_hash['gender'] #returns 'male'
my_hash['legal'] #returns true

Default Hash Value:

Another key difference between Arrays and Hashes is that Hashes allow you to store a default value if the key is not assigned to any value, like this:

my_hash = Hash.new('default value')
my_hash['gender'] = 'male'
puts my_hash['gender'] #'male'
puts my_hash['legal'] #'default value'
puts my_hash['height'] #'default value'

2d. What are some other Hash methods?

Summary:

3a. Key Differences

Whew, that was quite a bit of information!

Here’s a little table that summarizes the key differences between Arrays and Hashes:

Arrays Hashes
Create new: my_array = []
my_array = Array.new
my_hash = {}
my_hash = Hash.new
Accessing elements: Via an index of integers, starting at 0 Via assigned unique keys, which can be any object
Other unique factors: Can use symbols as keys
Can set a default value for non-assigned keys

3b. Still not sure what the differences are or when to use what?

Here is a good summary I found:

  • An array is an ordered list of things: a, b, c, d
  • A hash is a collection of key/value pairs: john has a peugeot, bob has a renault, adam has a ford
  • Source: Stack Overflow

If you need to store a collection of things, use an Array. If you need to store a collection of things that are associated with something else, use a Hash.

Feel free to reach out to me if you still have any questions!

This blog has been initially published on tonymai.github.io.