steven_noble
steven_noble

Reputation: 4213

Understanding the "||" OR operator in If conditionals in Ruby

Just briefly, why are the following three lines not identical in their impact?

if @controller.controller_name == "projects" || @controller.controller_name == "parts"

if @controller.controller_name == ("projects" || "parts")

if @controller.controller_name == "projects" || "parts"

The first gives me the result I want, but as there's actually more options than just projects and parts, using that form creates a verbose statement. The other two are more compact, but don't give me the same result.

Upvotes: 55

Views: 112077

Answers (9)

Victor Cordeiro Costa
Victor Cordeiro Costa

Reputation: 2194

I see a lot of people preferring the include? comparison.

I prefer to use the .in? operator. It is much more succinct. And also more readable, since we don't ask questions to the array, we ask questions to the variable you want to ask: On your case, the controller name.

@controller.controller_name.in? ["projects", "parts"]

Or, even better

@controller.controller_name.in? %w[projects parts]

Upvotes: 4

Logan Capaldo
Logan Capaldo

Reputation: 40346

The simple way to get a non verbose solution is

if ["a", "b", "c"].include? x

This actually has nothing to do with ||, but rather what values are considered to be true in ruby. Everything save false and nil is true.

Upvotes: 2

Martin DeMello
Martin DeMello

Reputation: 12346

the exact semantics of || are:

  • if first expression is not nil or false, return it
  • if first expression is nil or false, return the second expression

so what your first expression works out to is, if @controller.controller_name == "projects", then the expression short-circuits and returns true. if not, it checks the second expression. the second and third variants are essentially if @controller.controller_name == "projects", since "projects" || "parts" equals "projects". you can try this in irb:

>> "projects" || "parts"
=> "projects"

what you want to do is

if ["projects", "parts"].include? @controller.controller_name

Upvotes: 77

Lars Haugseth
Lars Haugseth

Reputation: 14881

The logical or operator || works on boolean expressions, so using it on strings doesn't do what you want.

There are several ways to achieve what you want that are less verbose and more readable.

Using Array#include? and a simple if-statement:

if ["projects", "parts"].include? @controller.controller_name
  do_something
else
  do_something_else
end

Using a case-statement:

case @controller.controller_name
when "projects", "parts" then
  do_something
else
  do_something_else
end

Upvotes: 2

madlep
madlep

Reputation: 49796

There's a few different things going on there:

if @controller.controller_name == "projects" || @controller.controller_name == "parts"

this gives the behaviour you want I'm assuming. The logic is pretty basic: return true if the controler name is either "projects" or "parts"

Another way of doing this is:

if ["projects", "parts", "something else..."].include? @controller.controller_name

That will check if the controller name is somewhere in the list.

Now for the other examples:

if @controller.controller_name == ("projects" || "parts")

This won't do what you want. It will evaluate ("projects" || "parts") first (which will result in "projects"), and will then only check if the controller name is equal to that.

if @controller.controller_name == "projects" || "parts"

This one gets even wackier. This will always result in true. It will first check if the controller name is equal to "projects". If so, the statement evaluates to true. If not, it evaluates "parts" on it's own: which also evaluates to "true" in ruby (any non nil object is considered "true" for the purposes of boolean logic")

Upvotes: 8

outis
outis

Reputation: 77450

Basically, == doesn't distribute over other operators. The reason 3 * (2+1) is the same as 3 * 2 + 3 * 1 is that multiplication distributes over addition.

The value of a || expression will be one of its arguments. Thus the 2nd statement is equivalent to:

if @controller.controller_name == "projects"

|| is of lower precedence than ==, so the 3rd statement is equivalent to:

if (@controller.controller_name == "projects") || "ports"

Upvotes: 3

jerhinesmith
jerhinesmith

Reputation: 15500

|| is also a null coalescing operator, so

"projects" || "parts"

will return the first string that is not null (in this case "projects"), meaning that in the second two examples, you'll always be evaluating:

if @controller.controller_name == "projects"

Firing up irb, you can check that this is happening:

a = "projects"
b = "parts"

a || b

returns projects

Upvotes: 6

vava
vava

Reputation: 25411

First one compares "projects" and "parts" string literals to @controller.controller_name variable.

Second one evaluates ("projects" || "parts") which is "projects" because "projects" string literal neither false or nil or empty string and compare it to @controller.controller_name

Third one compares @controller.controller_name and "projects" and if they are equal, it return true, if they aren't it return "parts" which is equal to true for if statement.

Upvotes: 0

Jim
Jim

Reputation: 5617

The difference is the order of what's happening. Also the || isn't doing what you think it does in the 2 and 3.

You can also do

if ['projects','parts'].include?(@controller.controller_name)

to reduce code in the future if you need to add more matches.

Upvotes: 11

Related Questions