All original content is created in Ukrainian. Not all content has been translated yet. Some posts may only be available in Ukrainian.Learn more

==, equal?, eql?, === in Ruby: what they check and when to use them

Post cover: ==, equal?, eql?, === in Ruby: what they check and when to use them
This content has been automatically translated from Ukrainian.
In Ruby, there are several ways to compare. They may seem similar at first glance, but they work quite differently.

== (value equality check)

  • Checks if the objects have the same content.
  • Often overridden in classes (String, Array, Hash) to compare values rather than identity.
a = "hello"
b = "hello"

a == b        # => true
a.equal?(b)   # => false
== asks: "Do these objects have the same content?"

equal? (object identity check)

  • Checks if this is the same object in memory.
  • Not overridden in classes, always compares object ids.
a = "hello"
b = "hello"
c = a

a.equal?(b)  # => false
a.equal?(c)  # => true
Here, to understand all this, you need to know what an object is in Ruby.

eql? (strict equality check for hashes and numbers)

  • Checks value and type.
  • Mainly used in hashes for keys.
1 == 1.0     # => true
1.eql?(1.0)  # => false, because different classes (Integer vs Float)
eql? is stricter than ==.

=== (case-match operator)

  • Used primarily in the case construct (we'll cover this below).
  • Behaves differently for different classes:
    • For classes (String, Integer) - checks if the object belongs to the class (is_a?).
    • In regular expressions - checks if the string matches the pattern.
    • In Range - checks if the object is within the range.
case 5
when 1..10
  "In range"   # => "In range"
end

String === "hello"  # => true
/ell/ === "hello"   # => true
Let's take a closer look at the case example. How does case … when work in Ruby?
Syntax:
case object
when pattern1
  code1
when pattern2
  code2
else
  default_code
end
What happens under the hood
Ruby does something like this:
if pattern1 === object
  code1
elsif pattern2 === object
  code2
else
  default_code
end
So when automatically uses === to compare with the case value. 
Next, examples for different types
Range
case 5
when 1..10
  "In range"
else
  "Out of range"
end
# => "In range"
It works as if Ruby executes (this is pseudocode):
(1..10) === 5  # => true
But it's important to note that ordinary use of === on numbers or other types does not necessarily work the same way:
10 === (1..20)   # => false
10 == (1..20)    # => false
(1..20) == 10    # => false
Here, Range#=== checks if the number is within the range.
Class
case "hello"
when String
  "It's a string"
when Integer
  "It's a number"
end
# => "It's a string"
Ruby executes:
String === "hello"   # => true
Integer === "hello"  # => false
For classes, === is shorthand for obj.is_a?(Class).
c) Regular expression
case "hello"
when /ell/
  "Match with pattern"
else
  "No match"
end
# => "Match with pattern"
Under the hood:
/ell/ === "hello"   # => true
For Regexp, === checks if the string matches the pattern.
We've strayed a bit from the topic of === and moved to case (because === is under the hood there). Such a simple operator, but it does different things.
To remember more easily:
  • case x; when y - this is not x == y, but y === x.
  • === works differently depending on the type of y:
    • Class -> is_a?
    • Range -> include?
    • Regexp -> =~ (string match)
This allows for very flexible condition writing.

This post doesn't have any additions from the author yet.

How self, protected, and private work (Ruby)
28 Oct 13:52

How self, protected, and private work (Ruby)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Integer division in Ruby: why 6 / 4 equals 1
28 Oct 14:10

Integer division in Ruby: why 6 / 4 equals 1

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
28 Oct 14:42

How &:to_s works in Ruby and what Symbol#to_proc is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What are Proc and Lambda in Ruby?
28 Oct 15:57

What are Proc and Lambda in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What happens if you call [1, 2, 3].map(&Person)
29 Oct 17:54

What happens if you call [1, 2, 3].map(&Person)

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Singleton class (eigenclass) in Ruby: what it is and why it is needed
29 Oct 18:29

Singleton class (eigenclass) in Ruby: what it is and why it is needed

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Include, Extend, Prepend in Ruby: how they work and what the difference is
29 Oct 21:20

Include, Extend, Prepend in Ruby: how they work and what the difference is

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
module_function in Ruby: when module methods are available as module methods and as functions
29 Oct 21:53

module_function in Ruby: when module methods are available as module methods and as functions

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
What is memoization in Ruby?
30 Oct 10:17

What is memoization in Ruby?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?
30 Oct 19:55

is_a?, kind_of?, instance_of? — how does Ruby check the type of an object?

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
&& vs and — the difference in Ruby that can break your code
30 Oct 20:23

&& vs and — the difference in Ruby that can break your code

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska
Variables in Ruby: @, @@ and class instance variable
30 Oct 20:54

Variables in Ruby: @, @@ and class instance variable

Нотатки про Ruby та RoR
Нотатки про Ruby та RoR@kovbaska