
Comparison
We've learned how to evaluate the truthiness of a statement and how to branch code based on that truthiness. The last foundational concept in conditional program flow is how to compare two variables. We need to be able to determine whether two variables are the same, are not the same, or are less than or greater than each other.
Let's look at the operators used for comparison in Ruby.
Comparison Operators
The following are the operators most commonly used for comparison in Ruby:

Figure 3.17: Comparison operators
Let's now write a method that combines all of the concepts in this chapter. We will be comparing two variables, x and y, and putting them through different comparison conditions:
def compare(x, y)
if x < y
puts "#{x} < #{y}"
elsif x <= y
puts "#{x} <= #{y}"
elsif x == y
puts "#{x} == #{y}"
elsif x >= y
puts "#{x} >= #{y}"
elsif x > y
puts "#{x} > #{y}"
end
end
While this code appears to represent all the cases of each operator, it's actually problematic. We won't possibly encounter the == case, because if we evaluate to true for <=, this will evaluate first and we won't encounter the == case. In fact, we won't even run into the >= case.
Hence, a better way to write a comparison method would be as follows:
def compare(x,y)
if x==y
puts "x and y are equal"
elsif x < y
puts "x is less than y"
else
puts "x is greater than y"
end
end
The choice of whether to use > and >= or < and <= depends on the situation and the problem you are trying to solve. This is called a boundary condition.
Comparing Strings
It is convenient to use comparison operators with integers, but it can be difficult with other types of variables, such as strings. Each object type (or class) in Ruby will define how to compare itself to other objects of the same type. For instance, when it comes to comparing two strings, we can think of it as follows:
"this string" == "something else" # false
That is straightforward, but the following condition is unrealistic:
"apples" > "oranges"
Let's use our compare method from the previous example to see how lots of different inputs behave:
compare("apples", "oranges") # x is less than y
compare("a", "a") # x and y are equal
compare("a", "A") # x is greater than y
compare("a", "b") # x is less than y
We are seeing some surprising things here. Why is a greater than A but less than b? The reason is because of how Ruby implements that comparison operator on strings. Most people will not memorize the internal algorithms of Ruby, so this is a good opportunity to read the docs.
When you go to the online Ruby documentation at https://packt.live/35mif9C, search for the type of object (or class) that you are looking for. In this case, string. There will be several results, usually for different versions of Ruby. Find your version of Ruby and dive into the string class. You will notice that there isn't an entry for <, >, or ==. Instead, Ruby implements all comparison operators with a single method: <=>, which returns -1, 0, and 1 to indicate less than, equal to, or greater than. All the other comparison operators (which really are just methods) are based on this central comparison operator. When you click on <=>, you will get an explanation of how Ruby decides to compare strings.
Exercise 3.02: Creating a Method to Check Your Balance
In this exercise, we will write a method to determine whether you have enough money to purchase something. We will determine the logic necessary for our balancer checker method and then implement it using conditionals and comparison operators:
- Open a new session on IRB.
- First, we determine the method. If you have more than enough money, you can make a purchase. If you have exactly enough money, you can make a purchase. If you do not have enough money, you cannot make a purchase:
def can_purchase?(amount_in_bank, cost_of_item)
if amount_in_bank >= cost_of_item
return true
else
return false
end
end
Here, we use the >= operator because as long as we have an amount equal to or greater than the cost of the item, we are okay to purchase it.
- We can also create an alternative implementation using a different operator:
def can_purchase?(amount_in_bank, cost_of_item)
if amount_in_bank < cost_of_item
return false
else
return true
end
end
Note
It's up to you as the programmer to decide which is easier to read. You will also notice that we named our method with a question mark. This is good practice with methods that return Boolean values.
- Let's see how we can call this method in a conditional:
bank_balance = 100
cost_of_item = 200
if can_purchase?(bank_balance, cost_of_item)
puts "You can purchase this item"
else
puts "Sorry, you don't have enough money to buy this item"
end
You can see how when you write code with clearly named variables and method names, the resulting code is quite readable, even to people who are new to Ruby.
Here's the expected output:
Figure 3.18: Output for balance checking
Thus, we have successfully created a method to initiate/decline a purchase after checking the balance amount of the bank account by using conditionals and comparison operators.
The case/when Statement
Often, when programming, you will need to branch code based on many conditions. Using lots of if/else statements can result in spaghetti code, which is not easy to read or understand.
Spaghetti code refers to a pejorative term where the code is unstructured and normally has many goto statements, making it difficult to interpret.
An alternative to lots of if/else statements is to use the case/when keywords. Here is an example:
def get_animal_sound(animal_type)
case animal_type
when :dog
"woof"
when :cat
"meow"
when :cow
"moo"
when :bird
"tweet"
else
nil
end
end
Here, we have a method that depends on the type of animal we pass into it and returns a string with the sound it makes. This works by doing a comparison of the animal_type variable against each value in the case/when statement, going from top to bottom.
Try calling the methods with the following values:
puts get_animal_sound(:dog) # woof
puts get_animal_sound(:bird) # tweet
puts get_animal_sound("dog") # nil
The last call did not return the correct sound because strings and symbols are unequal and so the execution will continue until it hits the final else condition in the block.
An alternative format in the case/when statement is to leave out the variable to the right of the case keyword. In this format, Ruby will execute the statement following an evaluation to true.
Consider the following example:
def get_animal_sound(animal_type)
case
when animal_type == :dog
"woof"
when animal_type == :cat
"meow"
when animal_type == :cow
"moo"
when animal_type == :bird
"tweet"
else
nil
end
end
In this case, the expanded format is less ideal than the first since we are repeating the animal_type variable for each condition. However, this format can come in handy when you don't know all the cases ahead of time or need to do more complex logic such as doing a greater than or less than comparison.
The === Operator
You might naturally wonder about the difference between == and ===. First, let's remember that the == operator is actually a method that each Ruby object type (class) defines for itself so it knows how to compare two objects of the same type. The === operator is an additional method that Ruby gives us to do a similar but slightly different equality comparison. The === operator is often called the "case equality" operator or the "identity" operator. It allows comparison based on the type of object passed into it. Another way to put it is that the === operator determines whether the input passed to it is part of the set of what it's being compared to.
Consider the following example:
def guess_type(input)
case input
when String
puts "It's a string!"
when Integer
puts "It's a number!"
when Array
puts "It's an array"
when Hash
puts "It's a hash"
else
puts "Not sure what you passed: #{input}"
end
end
guess_type(5)
guess_type("5")
guess_type([1,2,3])
guess_type({foo: :bar})
The output should look like this:

Figure 3.19: Output for input type determination
What's happening here is the case statement is calling the === operator on the input and determining whether the input is part of the set. In this case, it is determining whether the type of object passed in is a type that belongs to the class it's being compared to.
Here are some further examples of the === operator:
String === "mystring" # true
Integer === 5 # true
5 === Integer # false?
Here, you can see in the last statement that something strange is going on. Integer === 5 holds true but 5 === Integer is shown as false. The reason is, while 5 is a type of Integer, Integer is a class and is not a type of 5. This concept will become clearer as we cover classes and ranges in upcoming chapters.