How do I write this multi-line, complex condition if statement in Ruby?
if ( (aa != nil && self.prop1 == aa.decrypt)
|| (bb != nil && self.prop2 == bb.decrypt)
) && (self.id.nil? || self.id != id)
return true
end
I'm getting Syntax error; unexpected tOROP.
In Java, I could write
if (
( (aa != null && aa.prop1.equals(aa.decrypt()))
|| (bb != null && bb.prop2.equals(bb.decrypt()))
)
&& (this.id != id)
) {
return true;
}
The short answer is the operator needs to be at the end of the line in order to tell Ruby to continue reading the next line as part of the statement, so this would work:
if ( (aa != nil && self.prop1 == aa.decrypt) ||
(bb != nil && self.prop2 == bb.decrypt) ) &&
(self.id.nil? || self.id != id)
return true
end
That being said, you can probably reduce the logic by throwing exceptions based on input values, and removing some redundant checks (I'm making some jumps here about what your variables will look like, but you get the idea.)
raise 'aa must support decrypt' unless aa.respond_to? :decrypt
raise 'bb must support decrypt' unless bb.respond_to? :decrypt
if prop1 == aa.decrypt || prop2 == bb.decrypt
if self.id != id
return true
end
end
You need to escape whitespace with the backslash character, it's ugly but you need it if you want to split conditions to multiple lines. Either that or leave the boolean operator on the previous line. So either of these will work:
if ( (aa != nil && self.prop1 == aa.decrypt)\
|| (bb != nil && self.prop2 == bb.decrypt)\
) && (self.id.nil? || self.id != id)
return true
end
or:
if ( (aa != nil && self.prop1 == aa.decrypt) ||
(bb != nil && self.prop2 == bb.decrypt)) &&
(self.id.nil? || self.id != id)
return true
end
Personally I usually decide to put each or all conditions in a method that self documents what's being decided:
def everythings_cool?
( (aa != nil && self.prop1 == aa.decrypt) ||
(bb != nil && self.prop2 == bb.decrypt)) &&
(self.id.nil? || self.id != id)
end
then:
if everythings_cool?
# do stuff
Related
I have to implement a small part of a Rock, Paper, Scissors game for an assignment. Here is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError ; end
def self.winner(player1, player2)
# YOUR CODE HERE
puts "player1 = " + player1[1]
if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S')
raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
else
if player1[1] == player2[1]
return player1
elsif (player1[1] == 'R' && player2[1] == 'P') || (player1[1] == 'P' && player2[1] == 'S') || (player1[1] == 'S' && player2[1] == 'R')
return player2
else
return player1
end
end
end
def self.tournament_winner(tournament)
# YOUR CODE HERE
end
end
I am using rspec to test out the code and it breaks in both of the below cases. I am assuming that it has to do with where I check to make sure that the input is valid, and even though the input is valid, for whatever reason, the way I am doing the line if (player1[1] || player2[1] != 'R') || (player1[1] || player[2] != 'P') || (player1[1] || player2[1] != 'S') is making the input fail on every case. How can I fix that line so that if the input is valid, then it doesn't raise an error?
Failures:
1) RockPaperScissors game rock breaks scissors [10 points]
Failure/Error: raise NoSuchStrategyError.new("Strategy must be one of 'R','S','P'")
RockPaperScissors::NoSuchStrategyError:
Strategy must be one of 'R','S','P'
# ./lib/rock_paper_scissors.rb:11:in `winner'
# ./spec/rock_paper_scissors_spec.rb:10:in `block (3 levels) in <top (required)>'
EDIT: player1 and player2 are arrays with format ["playername", "move"] with the move being either R,S or P for rock, scissors or paper
The current code works:
def launched_city(country, city, city_link)
return 'current' if country == 'Malaysia' && ('Kuala Lumpur' == city_link)
return 'current' if country == 'Philippines' && ('Manila' == city_link)
if country == 'Australia'
return 'current' if city == 'Melbourne' && ('Melbourne' == city_link)
return 'current' if city == 'Sydney' && ('Sydney' == city_link)
return 'current' if city == 'Perth' && ('Perth' == city_link)
end
nil
end
but I think it's ugly. Any help?
I tried with case block. It failed with the case statement because I need to check the second statement. I also tried with if elsif else block. It's the same in this case.
COUNTRY_LINKS = { 'Malaysia'=>['Kuala Lumpur'],
'Philippines'=>['Manila'],
'Australia'=>['Melbourne', 'Sydney', 'Perth'] }
def launched_city(country, city, city_link)
if COUNTRY_LINKS.has_key?(country) && COUNTRY_LINKS[country].include? city_link) &&
(country != 'Australia' || city == city_link)
'current'
end
end
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I am struggling finding a way to verify these methods and was wondering if anyone knew a basic way to do this?
class PigLatinTest < MiniTest::Unit::TestCase
def test_word_beginning_with_a
assert_equal "appleay", PigLatin.translate("apple")
end
def test_other_word_beginning_e
assert_equal "earay", PigLatin.translate("ear")
end
def test_word_beginning_with_p
assert_equal "igpay", PigLatin.translate("pig")
end
For example the first one might be:
module PigLatin
class Word
def initialize(word)
#word = word.to_s
end
# remember to use the .to_s method
def translate(word)
if word[0] == "a" || "e" || "o" || "u" || "i"
word = word + "ay"
elsif word[0] != "a" || "e" || "o" || "u" || "i"
word = word-word[0]+"ay"
end
end
end
# you can add method here even outside of the class ...
end
------------in another file
module PigLatin
class Word
# remember to use the .to_s method
end
# you can add method here even outside of the class ...
end
Your translate method won't work. The problem is here:
if word[0] == "a" || "e" || "o" || "u" || "i"
and
elsif word[0] != "a" || "e" || "o" || "u" || "i"
You can't compare that way as the right side of either will not do what you think it will.
Some simple checks will show why there's something wrong:
'abc'[0] == "a" || "e" || "o" || "u" || "i" # => true
'efg'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'opq'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'xyz'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'abc'[0] != "a" || "e" || "o" || "u" || "i" # => "e"
'efg'[0] != "a" || "e" || "o" || "u" || "i" # => true
'opq'[0] != "a" || "e" || "o" || "u" || "i" # => true
'xyz'[0] != "a" || "e" || "o" || "u" || "i" # => true
Why are those wrong? Let's look at what's happening:
When the word starts with 'a', the test 'a' == 'a' is true:
'abc'[0] == "a" # => true
If we || ("or") true and something, we get true back because it was the first "true" value seen:
true || "e" # => true
If the first test failed, then || causes the second test to be evaluated, which in your code was "e", and wasn't a test, but Ruby didn't know that, and thought it was a "true" return value so it became the result of the expression:
false || "e" # => "e"
Knowing that, a correct way to write this would be:
'abc'[0] == "a" || 'abc'[0] == "e" || 'abc'[0] == "o" || 'abc'[0] == "u" || 'abc'[0] == "i" # => true
'efg'[0] == "a" || 'efg'[0] == "e" || 'efg'[0] == "o" || 'efg'[0] == "u" || 'efg'[0] == "i" # => true
'opq'[0] == "a" || 'opq'[0] == "e" || 'opq'[0] == "o" || 'opq'[0] == "u" || 'opq'[0] == "i" # => true
'xyz'[0] == "a" || 'xyz'[0] == "e" || 'xyz'[0] == "o" || 'xyz'[0] == "u" || 'xyz'[0] == "i" # => false
'abc'[0] != "a" && 'abc'[0] != "e" && 'abc'[0] != "o" && 'abc'[0] != "u" && 'abc'[0] != "i" # => false
'efg'[0] != "a" && 'efg'[0] != "e" && 'efg'[0] != "o" && 'efg'[0] != "u" && 'efg'[0] != "i" # => false
'opq'[0] != "a" && 'opq'[0] != "e" && 'opq'[0] != "o" && 'opq'[0] != "u" && 'opq'[0] != "i" # => false
'xyz'[0] != "a" && 'xyz'[0] != "e" && 'xyz'[0] != "o" && 'xyz'[0] != "u" && 'xyz'[0] != "i" # => true
however, that rapidly becomes hard to read and unwieldy, so something more concise is needed:
%w[a e o u].include? 'abc'[0] # => true
%w[a e o u].include? 'efg'[0] # => true
%w[a e o u].include? 'opq'[0] # => true
%w[a e o u].include? 'xyz'[0] # => false
!%w[a e o u].include? 'abc'[0] # => false
!%w[a e o u].include? 'efg'[0] # => false
!%w[a e o u].include? 'opq'[0] # => false
!%w[a e o u].include? 'xyz'[0] # => true
There is a problem with this though; As the array size increases, more loops are required to compare to the [0] value, which slows the code unnecessarily. A regular expression, written correctly, can get rid of that looping so the speed stays very constant:
'abc'[0][/[aeou]/] # => "a"
'efg'[0][/[aeou]/] # => "e"
'opq'[0][/[aeou]/] # => "o"
'xyz'[0][/[aeou]/] # => nil
Notice though, that instead of getting true/false, the results are the character matched by the pattern or nil. In Ruby, only nil and false are considered false values, and everything else is true, so we can translate those into true, true, true, false respectively, but by taking advantage of the ! operator we can make it even more clear:
!!'abc'[0][/[aeou]/] # => true
!!'efg'[0][/[aeou]/] # => true
!!'opq'[0][/[aeou]/] # => true
!!'xyz'[0][/[aeou]/] # => false
It might seem that we'd have to use !!! to "not" the results like we'd want when using !=, but that isn't necessary. A single ! will do the same thing:
!'abc'[0][/[aeou]/] # => false
!'efg'[0][/[aeou]/] # => false
!'opq'[0][/[aeou]/] # => false
!'xyz'[0][/[aeou]/] # => true
But wait! There's more! Even that can be improved upon a slight amount by removing the string slice ([0]) and using a regex anchor. Compare these two, and their benchmark:
require 'fruity'
ALPHABET = ('a'..'z').to_a.join
compare do
slice_it { ALPHABET[0][/[aeou]/] }
regex_it { ALPHABET[/^[aeou]/] }
end
# >> Running each test 8192 times. Test will take about 1 second.
# >> regex_it is faster than slice_it by 39.99999999999999% ± 10.0%
So, using something like:
'abc'[/^[aeou]/] # => "a"
!'abc'[/^[aeou]/] # => false
!!'abc'[/^[aeou]/] # => true
will be fast and compact and let you test to see what a string starts with.
I have a validate_completion method defined in my task.rb model. The Pseudo logic should be
if no hazards exist
date_completed = Date.today
elsif any hazard exists with a risk_total > 1000
date_completed = nil
else any hazard that exists has a risk_total =< 1000
date_complete = Date.today
end
The code looks like as follows.
def validate_completion
if self.date_completed.nil?
if self.Biohaz_exist == "No"
if self.Hazmat_exist == "No"
if self.Fire_exist == "No"
if self.Hazengy_exist == "No"
if self.Tool_exist == "No"
if self.Rad_exist == "No"
if self.Laz_exist == "No"
if self.Mag_exist == "No"
if self.Ergo_exist == "No"
if self.Mechand_exist == "No"
if self.Road_exist == "No"
if self.Fall_exist == "No"
if self.Hazatm_exist == "No"
if self.Noise_exist == "No"
if self.Ovrhead_exist == "No"
if self.Cut_exist == "No"
if self.Temp_exist == "No"
if self.Access_exist == "No"
if self.Cowrkr_exist == "No"
if self.Lonewrkr_exist == "No"
self.date_completed = Date.today
elsif self.Biohaz_exist == "Yes" and self.Biohaz.risk_total > 1000
self.date_completed = nil
elsif self.Hazmat_exist == "Yes" and self.Hazmat.risk_total > 1000
self.date_completed = nil
elsif self.Fire_exist == "Yes" and self.Fire.risk_total > 1000
self.date_completed = nil
elsif self.Hazengy_exist == "Yes" and self.Hazengy.risk_total > 1000
self.date_completed = nil
elsif self.Tool_exist == "Yes" and self.Tool.risk_total > 1000
self.date_completed = nil
elsif self.Rad_exist == "Yes" and self.Rad.risk_total > 1000
self.date_completed = nil
elsif self.Laz_exist == "Yes" and self.Laz.risk_total > 1000
self.date_completed = nil
elsif self.Mag_exist == "Yes" and self.Mag.risk_total > 1000
self.date_completed = nil
elsif self.Ergo_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Mechand_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Road_exist == "Yes" and self.Ergo.risk_total > 1000
self.date_completed = nil
elsif self.Fall_exist == "Yes" and self.Fall.risk_total > 1000
self.date_completed = nil
elsif self.Hazatm_exist == "Yes" and self.Hazatm.risk_total > 1000
self.date_completed = nil
elsif self.Noise_exist == "Yes" and self.Noise.risk_total > 1000
self.date_completed = nil
elsif self.Ovrhead_exist == "Yes" and self.Ovrhead.risk_total > 1000
self.date_completed = nil
elsif self.Cut_exist == "Yes" and self.Cut.risk_total > 1000
self.date_completed = nil
elsif self.Temp_exist == "Yes" and self.Temp.risk_total > 1000
self.date_completed = nil
elsif self.Access_exist == "Yes" and self.Access.risk_total > 1000
self.date_completed = nil
elsif self.Cowrkr_exist == "Yes" and self.Cowrk.risk_total > 1000
self.date_completed = nil
elsif self.Lonewrkr_exist == "Yes" and self.Lonewrkr.risk_total > 1000
self.date_completed = nil
else
self.date_completed = Date.today
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
As is right now if no hazards exist, it successfully sets the date to today. And if I then add a hazard it effectively switches the date back to nil. However, If one hazard exists and it has a risk_total below 1000 the date remains nil.
"Well, given that your hazards are statically defined and not in the database, you could do something like this:
#definition of all the hazard
hazards = [:Fire, :Road, :Fall]
def validate_completion
# find all the hazards that exist
exists = hazards.select { |hazard| self.send("#{hazard.to_s}_exist") }
# find if at least one high risk hazard exists
high_risk_total = exists.detect {|hazard| self.send("#{hazard.to_s}_risk_total") > 1000 }
# find if at least one low rick hazard exists
low_risk_total = exists.detect {|hazard| self.send("#{hazard.to_s}_risk_total") <= 1000 }
# validation logic here
end
You can get by with this logic, but it might be significantly easier to make your hazards a database table and use ActiveRecord associations and queries to simplify this.
I am not sure what's happening here...but say I do this:
def who_wins?(choice1, choice2)
if (choice1 == 'R' && choice2 == 'S') || (choice1 == 'S' && choice2 == 'P') || (choice1 == 'P' && choice2 == 'R')
return choice1
elsif choice1 == choice2
return "tie"
else
raise NoSuchStrategyError
end
end
won_wins?('R', 'P')
It gives me the following error:
NoMethodError: undefined method `won_wins?' for main:Object
at top level in my-file.rb at line 25
Why would it do that, even though I am calling the method AFTER the definition?
You typed won_wins? and not who_wins?