1. Technology
You can opt-out at any time. Please refer to our privacy policy for contact information.

The Case Equality Operator

By

The case equality operator === (three equals signs) is not well defined. In fact, for many classes, it's not really defined at all and depending on the class does something completely different. It's not a straight up equality operator like ==, you can think of it as the "fuzzy equality operator." Let me show you with an example.

Regular expressions define the case equality operator to evaluate to true if its operand matches the regular expression. In other words, it's roughly equivalent to the =~ operator. This is demonstrated in the following IRB session.


irb(main):001:0> /test[0-9]+/ === "test90584"
=> true
irb(main):002:0> /[A-Z][a-z]+/ === "Capitalized"
=> true

Another useful example is the === operator when applied to ranges. Remember, a range literal is any two numbers (or other objects, but generally only numbers) separated by the .. or … operator, and any number that falls between these two numbers is within the range. The === operator, when applied to ranges, is the same as calling the range's include? method.


irb(main):001:0> (1..10) === 5
=> true
irb(main):002:0> (1..10) === 50
=> false
irb(main):003:0> (1..10).include?(7)
=> true

The following is a table of some of the types and what they do when the === operator is applied to them. Remember that what the === operator does depends on the type of the object on the left hand side of the operator. So "test" === 10 will use the String#=== method. If you ever encounter an object not listed here, you can look at that object's class' === method, or that of its ancestor. If there is no === defined, then Object#=== is used, its function is described below.

  • Class#=== - Classes are actually a subclass of Module, so Module#=== is used. Module#=== evaluates to true if the right-hand argument's type is the same as or a descendant of the left-hand side. For example, Numeric === "10" would be false, but Numeric === 10 would be true. This is used in case statements to decide what type of object has been passed.
  • Range#=== - The equivalent of calling left_hand_side.include?(right_hand_side). Ranges can be of many different types, including strings and numerics. However, if the right hand side is of a different type (such as (1..10) === "test", then Range#=== will always evaluate to false.
  • Regexp#=== - Equivalent to the Regexp#=~, evaluating to true if the regular expression matches anywhere within the string. If the regular expression doesn't match, or the right-hand side of the expression is not a string, it will evaluate to false.
  • Proc#=== - If a Proc object is on the left-hand side of the === operator, the Proc object is invoked with the right-hand side of the expression being the parameter for the invocation. The expression itself evaluates to whatever the proc object evaluates to. So, for example, you could say proc {|a| a**2 == 25 } == -5 and it would evaluate to true, since negative five squared is 25. Note that this is only valid for Ruby 1.9.x, Ruby 1.8.x does not define Proc#===.
  • String#=== - Defined as String#==, will evaluate to true if the two strings are equal. Will always evaluate to false if the right-hand side is not a string.
  • Symbol#=== - Defined as Symbol#==, will evaluate to true if the two symbols are the same symbol. Like String#==, will always evaluate to false if the right hand side is not a symbol.
  • Fixnum#===, Float#=== and Bignum#=== - Evaluates to true if the right hand side is the same as the left hand side numerically. This is not quite the same as the three class' == operators, which often require the right hand side to be of the same type (floats can only be equal to floats, for example). This is relaxed a bit so this operator may be more easily used in case statements.
  • Object#=== - Though you'll probably never be calling this one yourself, if no class in the left-hand side's class lineage has defined a === operator, then the Object#=== operator will be used. Object#=== evaluates to true if the two objects refer to the same object (they have the same object id). If that fails, it will attempt to call left_hand_side == right_hand_side.

Using this operator outside of case statements is not commonplace, but perfectly acceptable. Just always remember that the operator behaves differently depending on the left-hand side of the expression. Also note that this operator has higher precedence than some other operators, most notably the .. and … operators (which are used to create Range objects). So, in order to use this on a Range object, you'll have to add some parentheses. The 1..10 === a expression will generate an error as it's equivalent to 1..(10 === a) because === has a higher precedence than the .. operator. To fix this, add some parentheses around the range operator: (1..10) === a.

  1. About.com
  2. Technology
  3. Ruby
  4. Beginning Ruby
  5. Control Structures
  6. The Case Equality Operator

©2014 About.com. All rights reserved.