Ruby Comparison Operators? == VS. ===

=== vs. == in Ruby

The two really have nothing to do with each other. In particular, #== is the equality operator and #=== has absolutely nothing to with equality. Personally, I find it rather unfortunate that #=== looks so similar to #==, uses the equals sign and is often called the case equality operator, triple equals operator or threequals operator when it really has nothing to do with equality.

I call #=== the case subsumption operator (it's the best I could come up with, I'm open to suggestions, especially from native English speakers).

The best way to describe a === b is "if I have a drawer labeled a, does it make sense to put b in it?"

So, for example, Module#=== tests whether b.is_a?(a). If you have Integer === 2, does it make sense to put 2 in a box labeled Integer? Yes, it does. What about Integer === 'hello'? Obviously not.

Another example is Regexp#===. It tests for a match. Does it make sense to put 'hello' in a box labeled /el+/? Yes, it does.

For collections such as ranges, Range#=== is defined as a membership test: it makes sense to put an element in a box labeled with a collection if that element is in the collection.

So, that's what #=== does: it tests whether the argument can be subsumed under the receiver.

What does that have to with case expressions? Simple:

case foo
when bar
baz
end

is the same as

if bar === foo
baz
end

What's the difference between equal?, eql?, ===, and ==?

I'm going to heavily quote the Object documentation here, because I think it has some great explanations. I encourage you to read it, and also the documentation for these methods as they're overridden in other classes, like String.

Side note: if you want to try these out for yourself on different objects, use something like this:

class Object
def all_equals(o)
ops = [:==, :===, :eql?, :equal?]
Hash[ops.map(&:to_s).zip(ops.map {|s| send(s, o) })]
end
end

"a".all_equals "a" # => {"=="=>true, "==="=>true, "eql?"=>true, "equal?"=>false}


== — generic "equality"

At the Object level, == returns true only if obj and other are the same object. Typically, this method is overridden in descendant classes to provide class-specific meaning.

This is the most common comparison, and thus the most fundamental place where you (as the author of a class) get to decide if two objects are "equal" or not.

=== — case equality

For class Object, effectively the same as calling #==, but typically overridden by descendants to provide meaningful semantics in case statements.

This is incredibly useful. Examples of things which have interesting === implementations:

  • Range
  • Regex
  • Proc (in Ruby 1.9)

So you can do things like:

case some_object
when /a regex/
# The regex matches
when 2..4
# some_object is in the range 2..4
when lambda {|x| some_crazy_custom_predicate }
# the lambda returned true
end

See my answer here for a neat example of how case+Regex can make code a lot cleaner. And of course, by providing your own === implementation, you can get custom case semantics.

eql?Hash equality

The eql? method returns true if obj and other refer to the same hash key. This is used by Hash to test members for equality. For objects of class Object, eql? is synonymous with ==. Subclasses normally continue this tradition by aliasing eql? to their overridden == method, but there are exceptions. Numeric types, for example, perform type conversion across ==, but not across eql?, so:

1 == 1.0     #=> true
1.eql? 1.0 #=> false

So you're free to override this for your own uses, or you can override == and use alias :eql? :== so the two methods behave the same way.

equal? — identity comparison

Unlike ==, the equal? method should never be overridden by subclasses: it is used to determine object identity (that is, a.equal?(b) iff a is the same object as b).

This is effectively pointer comparison.

Difference between = and == in Ruby?

== only measures if two objects are equal, whereas <=> should return -1 if the first object is smaller, 0 if they are equal, and 1 if the first object is greater.

If you define a <=> method for your class, you'll get all of the other comparison operators defined as well (==, <, >, and so on).

Ruby Equal to vs Comparison;

When you want to compare? It returns -1, 0, 1. For example, sorting users by first name:

users_by_first = users.sort { |u1, u2| u1.fname <=> u2.fname }

Here's another SO question asking about the spaceship operator.

What does the === operator do in Ruby?

Just like with every other method in Ruby (or actually pretty much any object-oriented language),

a === b

means whatever the author of a's class wants it to mean.

However, if you don't want to confuse the heck out of your colleagues, the convention is that === is the case subsumption operator. Basically, it's a boolean operator which asks the question "If I have a drawer labelled a would it make sense to put b in that drawer?"

An alternative formulation is "If a described a set, would b be a member of that set?"

For example:

 (1..5) === 3           # => true
(1..5) === 6 # => false

Integer === 42 # => true
Integer === 'fourtytwo' # => false

/ell/ === 'Hello' # => true
/ell/ === 'Foobar' # => false

The main usage for the === operator is in case expressions, since

case foo
when bar
baz
when quux
flurb
else
blarf
end

gets translated to something (roughly) like

_temp = foo

if bar === _temp
baz
elsif quux === _temp
flurb
else
blarf
end

Note that if you want to search for this operator, it is usually called the triple equals operator or threequals operator or case equality operator. I really dislike those names, because this operator has absolutely nothing whatsoever to do with equality.

In particular, one would expect equality to be symmetric: if a is equal to b, then b better be also equal to a. Also, one would expect equality to be transitive: if a == b and b == c, then a == c. While there is no way to actually guarantee that in a single-dispatch language like Ruby, you should at least make an effort to preserve this property (for example, by following the coerce protocol).

However, for === there is no expectation of either symmetry or transitivity. In fact, it is very much by design not symmetric. That's why I don't like calling it anything that even remotely resembles equality. It's also why I think, it should have been called something else like ~~~ or whatever.

String || comparison operator in Ruby

'X' || 'O' just says X or O. And since any string is truthy it always returns X. So any spot where you’ve said [('X' || 'O')], you’ve really just said ['X'].

Because of this, you’re only ever checking if a whole line of 3 is all X.

I don’t really understand how you’re trying to test this, but I feel like you’d be better off running the function twice first handing in X and then handing in O if it didn’t find X as a winner, as opposed to trying to check both as once.

Alternatively you could instead have the function return 'X', 'O', or nil and then you could have it only run the function once. The string returned would be who won and if it’s nil then no one won. I would also recommend making a loop for this. I find it easier to read.

Here's how I would solve the problem.

ROWS = [
[1,2,3],
[4,5,6],
[7,8,9],

[1,4,7],
[2,5,8],
[3,6,9],

[1,5,9],
[7,5,3],
]

def find_winner(board)
ROWS.each do |row|
# subtract 1 because the ROWS is 1-indexed (like a phone dial) but arrays are 0-indexed
row_values = row.map { |v| board[v - 1] }
return('X') if row_values.all?('X')
return('O') if row_values.all?('O')
end

return(nil)
end

test1 = [
'X', 'X', 'X',
'O', 'X', 'O',
'O', 'O', '',
]
puts "Winner of test1: #{find_winner(test1).inspect}"
"X"

test2 = [
'X', '', 'X',
'X', 'O', 'O',
'X', 'O', 'X',
]
puts "Winner of test2: #{find_winner(test2).inspect}"
"X"

test3 = [
'O', 'X', 'X',
'O', 'X', 'O',
'O', 'O', '',
]
puts "Winner of test3: #{find_winner(test3).inspect}"
"O"

test4 = [
'O', 'X', 'O',
'X', 'O', 'X',
'O', 'O', 'X',
]
puts "Winner of test4: #{find_winner(test4).inspect}"
"O"

test5 = [
'O', 'X', 'O',
'O', 'X', 'O',
'X', 'O', 'X',
]
puts "Winner of test5: #{find_winner(test5).inspect}"
nil

Ruby string compare: == vs ===

They are not the same. The short answer is that == checks if the values are the same, but type-casts if necessary. === is only true if the values AND types are the same.

0 == "0" #=> true
0 === "0" #=> false
0 === 0 #=> true

There are (literally) millions of discussions about this on the internet, as this is not, by a long-shot, a Ruby-specific thing. Try a Google search if you'd like more information.

Edit

I made a mistake, this is incorrect.

ruby how to use || (or-equals) operator in parentheses after == to match multiple expressions, i.e. x == (5 || 6)

Like this:

def demo(x)
puts "foo!" if x == 5 || x == 7
end

x == (5 || 7) won't work because 5 || 7 is 5, so it's the same as x == 5.

3 Equals or Case Equality operator

The simple answer is: because it doesn't make sense. The relationship the operator describes is not commutative, why should the operator be?

Just look at your own examples: 5 is an Integer. But is Integer a 5? What does that even mean?

=== is the case subsumption operator, and subsumption doesn't commute.

The fact that the case subsumption operator uses equals signs, and that it is usually called the triple equals, threequals or case equality operator is terribly unfortunate since it not only has absolutely nothing to do with equality, but it also does not conform to many of the laws that equality conforms to, such as transitivity and as you mentioned commutativity.

For more of my ranting about === see

  • What does the === operator do in Ruby?
  • === vs. == in Ruby
  • How does Integer === 3 work?


Related Topics



Leave a reply



Submit