=== 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 ifobj
andother
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 ifobj
andother
refer to the same hash key. This is used byHash
to test members for equality. For objects of classObject
,eql?
is synonymous with==
. Subclasses normally continue this tradition by aliasingeql?
to their overridden==
method, but there are exceptions.Numeric
types, for example, perform type conversion across==
, but not acrosseql?
, 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
==
, theequal?
method should never be overridden by subclasses: it is used to determine object identity (that is,a.equal?(b)
iffa
is the same object asb
).
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
Fresh Install of Rvm in Ubuntu Isn't Letting Me Install Gems (Zlib Error)
When Trying to Generate a Model with Rails and Postgresql, the Command Hangs Without Error
Linux Cli: How to Render Arabic Text into Bitmap
Ruby Yaml Parser by Passing Constructor
Cannot Install Ruby Gems - Zlib Error
Alter Rails Params Hash from Rack Middleware
Ruby Metaprogramming, How Does Rspec's 'Should' Work
Ruby on Rails:How to Implement Cancel Button in Form_Tag
Ruby on Rails Foreach with Bootstrap3 Row Class
Generating Sequential Numbers in Multi-User Saas Application
Implicit Argument Passing of Super from Method Defined by Define_Method() Is Not Supported
Zip Up All Paperclip Attachments Stored on S3
Rails Routing: Giving Default Values for Path Helpers
Watermark in Existing PDF in Ruby
Determining Whether One Array Contains the Contents of Another Array in Ruby
Argument Out of Range Rails 4 and Bootstrap3-Datetimepicker-Rails