LookingForCheese
LookingForCheese

Reputation: 11

Why isn't my || or condition not working here?

I'm learning on codeacademy and I am trying to make a redacted program work for each situation including periods and commas next to a redacted word. My code that works as intended:

puts "Please enter a value"
text = gets.chomp
puts "To redact?"
redact = gets.chomp
words = text.split(" ")
words.each do |foo|
if foo.downcase == redact
    print "REDACTED "
elsif foo.downcase == (redact + ".")
    print "REDACTED" + "." + " "
elsif foo.downcase == (redact + ",")
    print "REDACTED" + "," + " " 
else
    print foo + " "
end

end

I get this in the console:

Please enter a value
 I am master, right?
To redact?
 master
I am REDACTED, right? ["I", "am", "master,", "right?"]

But, the code that does not work:

puts "Please enter a value"
text = gets.chomp
puts "To redact?"
redact = gets.chomp
words = text.split(" ")
words.each do |foo|
if foo.downcase == redact
    print "REDACTED "
elsif (foo.downcase == (redact + ".") || (redact + ","))
    print "REDACTED" + "." + " "
else
    print foo + " "
end
end

For the time being, ignore the fact that it would print a period even if it was supposed to be a comma. I'm curious about why when I run this program, every word is redacted in the console.

Upvotes: 0

Views: 54

Answers (3)

AnoE
AnoE

Reputation: 8355

Explanation

elsif (foo.downcase == (redact + ".") || (redact + ","))

Let's break it down like the Ruby compiler would:

  • elsif XXX looks for a boolean value XXX (that is, in Ruby talk, it looks at the truthiness of XXX)
    • foo.downcase == (redact + ".") || (redact + ",") is evaluated. == binds less tight than ||, so the term is split on the == first
      • foo.downcase returns a string
      • (redact + ".") || (redact + ",") is just the operator || which is defined as "if the left operand is truthy, then evaluate to the left operand; else the right one". So ruby looks at the left operand:
        • redact + "." is a non-empty string which by definition is always truthy, so the result of the || is the left side (no matter what redactcontains).

So, traveling back up the tree, (redact + ".") || (redact + ",") is evaluated to redact + "." which is compared to foo.downcase with the result you're experiencing.

Suggestion

A rubyish way to express what you mean by elsif (foo.downcase == (redact + ".") || (redact + ",")) would be:

elsif [ redact+".", redact+"," ].include? foo.downcase

(Or, alternatively, any number of other solutions, like regular expressions, string splitting etc. - but this variant would be closest to what you wrote, and pretty easy to read for someone familiar with Ruby idioms. Since it involves creating multiple strings and an array, this is not suggested in performance critical loops, but fine if performance/memory use is not an issue.)

Upvotes: 0

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121020

Instead of [erroneous] check for foo being either this or that, one might use regular expression:

foo.downcase =~ /\A#{redact}[.,]\z/

The above will be evaluated to truthy when foo.downcase matches the value of redact followed by either a comma or a dot.

Upvotes: 0

T. Claverie
T. Claverie

Reputation: 12256

The statement foo.downcase == (redact + ".") || (redact + ",") is always true in ruby. The equivalent of this statement is: foo.downcase == (redact + ".") || (redact + ",") != nil

What you want to test instead is:

foo.downcase == (redact + ".") || foo.downcase == (redact + ",")

Ruby can evaluate anything as a boolean. Only nil and False will be evaluated to false.

Upvotes: 1

Related Questions