Ruby Multiple AND Evaluations For Integer Value - ruby

I have, on a few occasions, found myself needing to write a rather verbose if statement in some ruby scripts.
The statement would look something like this:
if long_var_name == 0 && very_different_name == 0 && other_third_var == 0 && additional_variable == 0 && name_five == 0 && longest_variable_name_six == 0
# (possibly even more conditions)
# do stuff here...
end
It seems like there has to be a more elegant way to do this.
The problem is, if and and aren't exactly easy to research with google, as basic english words. So I've come up empty-handed.
Does anyone know a way to shorten this kind of situation?
It can become a nightmare to read when you have even more of them.
Sometimes variable renaming isn't an option.
The names are sometimes vastly different.
Note: I found a clever solution for similar situations with OR:
Ruby Multiple OR Evaluations For String Value

If you have an array and are specifically testing for zero, you can do:
vars1 = [0,0,3,0,0]
vars2 = [0,0,0,0,0]
vars1.all?(&:zero?) # false
vars2.all?(&:zero?) # true
EDIT: Based on OP's added conditions of having different names for the values:
if [long_var_name_1,long_var_name_2,long_var_name_3].all?(&:zero?)
# do stuff here...
end

In your specific case, I would write
if [a, b, c, d, e, f].all? { |var| var == 0 }
There's noting wrong about chaining and conditions IMHO.

Have you thought of breaking it up into logic expressions? Basically break it up into smaller bits of logical groupings and its easier to read e.g.
grp_1_zero = var_1 == 0 && var_2 == 0 && var_3 == 0
grp_2_zero = var_a == 0 && var_b == 0 && var_c == 0
grp_3_zero = var_z == 0 && var_x == 0 && var_y == 0
if grp_1_zero && grp_2_zero && grp_3_zero
#code here
end

Another, for array a:
a == Array.new(a.size,0)

You can use Enumerable#all? and the Symbol#to_proc utilisation of the Fixnum#zero? method.
foo = 0
bar = 0
baz = 0
[foo, bar, baz].all? &:zero?
# => true
one = 1
two = 2
[foo, one, two].any? &:zero?
#=> true
Note that you can also provide any anonymous function for the test.
owns_peanuts = ->(person){ person.inventory.contains :peanuts }
[bill, david, mike].any? &owns_peanuts

Related

Combining multiple 'elsif' statements

I try to programm snakes in Ruby. In order to get myself more familiar with Ruby. I define the position of every part of the snake through saving its X and Y value in two 1D arrays one for a X value and one for a Y value.
$x = [2,...]
$y = [2,...]
(What I forgot to tell is that the head of the Snake moves through user input while the rest just inherits its position from the part before like this.)
def length(f)
if $length >= f
$y[f] = $y[f-1]
$x[f] = $x[f-1]
end
end
In order to get a field for the Snake to move around I programmed this.
for a in (1..20)
for b in (1..20)
print " X "
end
puts" "
end
Which gives me a 20*20 field.
I then tried to display every part of the snake like on the field like this.(While also drawing a boarder around the field.)
for a in (1..20)
for b in (1..20)
if a == 1 || a == 20
if b == 1 || b == 20
print " + "
else
print " - "
end
elsif b == 1 || b == 20
print " | "
elsif a == $x[0] && b == $y[0]
body
elsif a == $x[1] && b == $y[1]
body
elsif a == $x[2] && b == $y[2]
body
elsif a == $x[3] && b == $y[3]
body
elsif a == $x[4] && b == $y[4]
body
else
print " "
end
end
puts""
end
This works but if the user is really good/ has a lot of spare time I need to make allot of elsif for every one represents a part of the snake if the snake should have as a limit a length of 100 I would need to make 100 elsif statements.(The body is just:
def body
print " # ".green
end
)
I tried fixing it with a for loop like this:
for c in (1..100)
if a == $x[c] && b == $y[c]
body
end
end
and this
loop do
$x.size.times do |index|
if $x[index] == a && $y[index] == b
body
end
end
break
end
But sadly this didn't gave the desired outcome for this interfered with the ifthat draws the boarders of the field.
Is there a way to combine these multiple elsif statements?
Every help would be highly appreciated. ( Sorry for being to vague in the first draft.)
Recommended Refactorings
NB: You included no sample data in your original post, so your mileage with answers will vary.
You have a number of issues, not just one. Besides not being DRY, your code is also not very testable because it's not broken out into discrete operations. There are a number of things you can (and probably should) do:
Break your "body" stuff into discrete methods.
Use Array or Enumerator methods to simplify the data.
Use dynamic methods to loop over your arrays, rather than fixed ranges or for-loops.
Use case/when statements inside your loop to handle multiple conditionals for the same variable.
In short, you need to refactor your code to be more modular, and to leverage the language to iterate over your data objects rather than using one conditional per element as you're currently doing.
Simplify Your Data Set and Handle Procedurally
As an example, consider the following:
def handle_matched_values array
end
def handle_mismatched_values array
end
paired_array = a.zip b
matched_pairs = paired_array.select { |subarray| subarray[0] == subarray[1] }
unmatched_pairs = paired_array.reject { |subarray| subarray[0] == subarray[1] }
matched_pairs.each { |pair| handle_matched_values pair }
matched_pairs.each { |pair| handle_mismatched_values pair }
In this example, you may not even need an if statement. Instead, you could use Array#select or Array#reject to find indices that match whatever criteria you want, and then call the relevant handler for the results. This has the advantage of being very procedural, and makes it quite clear what data set and handler are being paired. It's also quite readable, which is extremely important.
Dynamic Looping and Case Statements
If you truly need to handle your data within a single loop, use a case statement to clean up your conditions. For example:
# Extract methods to handle each case.
def do_something_with data; end
def do_something_else_with data; end
def handle_borders data; end
# Construct your data any way you want.
paired_array = a.zip b
# Loop over your data to evaluate each pair of values.
paired_array.each do |pair|
case pair
when a == b
do_something_with pair
when a == paired_array.first || paired_array.last
handle_borders pair
else
do_something_else_with pair
end
end
There are certainly plenty of other ways to work pairwise with a large data set. The goal is to give you a basic structure for refactoring your code. The rest is up to you!
I would start with something like this:
(1..20).each do |a|
(1..20).each do |b|
if [1, 20].include?(a)
print([1, 20].include?(b) ? ' + ' : ' - ')
elsif (1..100).any? { |i| a == $x[i] && b == $y[i] }
body
else
print(' ')
end
puts('')
end
end
I suppose this would work as a solution even if it is not that advanced?
loop do
$x.size.times do |index|
if $x[index] == a && $y[index] == b
body
end
end
break
end

Ruby interpreter optimization

I was looking for information on Ruby's interpreter. I know it's an interpreted language and I wanted information as to whether optimization was done on the fly. A simple example would be the fiz-buzz problem. Does the interpreter convert trying to solve x%3 == 0 && x%5 == 0 to x%15 == 0 ?
It is easy to see gcc flags and what they do. Is there somewhere I can see the same for ruby?
What does the interpreter do exactly?
Since it is an interpreted language, it will run through the code one line at a time, until the first error. There are some optimizations as to how each line is evaluated, but these should also appear in other languages. e.g. lazy evaluation:
false && x>4 && x!=0
after the false followed by logical 'and', it just ignores the rest. You can have the interpreter load certain things on startup in your ~/.irbrc , if you wish.
Not really an answer, but a counter-example:
class Fixnum
def %(obj)
obj < 10 ? 0 : 1
end
end
x = 15
x % 3 == 0 && x % 5 == 0
# => true
x % 15 == 0
# => false
It's hard to optimize this expression without knowing what x is or how x#% is implemented.

Simple way to test if multiple variables have same value, ruby

Is there a simple way of testing that several variables have the same value in ruby?
Something linke this:
if a == b == c == d #does not work
#Do something because a, b, c and d have the same value
end
Of course it is possible to check each variable against a master to see if they are all true, but that is a bit more syntax and is not as clear.
if a == b && a == c && a == d #does work
#we have now tested the same thing, but with more syntax.
end
Another reason why you would need something like this is if you actually do work on each variable before you test.
if array1.sort == array2.sort == array3.sort == array4.sort #does not work
#This is very clear and does not introduce unnecessary variables
end
#VS
tempClutter = array1.sort
if tempClutter == array2.sort && tempClutter == array3.sort && tempClutter == array4.sort #works
#this works, but introduces temporary variables that makes the code more unclear
end
Throw them all into an array and see if there is only one unique item.
if [a,b,c,d].uniq.length == 1
#I solve almost every problem by putting things into arrays
end
As sawa points out in the comments .one? fails if they are all false or nil.
tokland suggested a very nice approach in his comment to a similar question:
module Enumerable
def all_equal?
each_cons(2).all? { |x, y| x == y }
end
end
It's the cleanest way to express this I've seen so far.
How about:
[a,b,c,d] == [b,c,d,a]
Really just:
[a,b,c] == [b,c,d]
will do
a = [1,1,1]
(a & a).size == 1 #=> true
a = [1,1,2]
(a & a).size == 1 #=> false
[b, c, d].all?(&a.method(:==))

Solving Fizzbuzz Using Rubys Enumerable Module?

Hey I was given the fizzbuzz task recently and I had answered with the usual,
if ((i%3==0) || (i.to_s.include?('3'))) && ((i%7==0) || (i.to_s.include?('7')))
p 'Fizzbuzz'
elsif (i%3==0) || (i.to_s.include?('3'))
p 'Fizz'
elsif (i%7==0) || (i.to_s.include?('7'))
p 'Buzz'
else
p i
end
and when asked to shorten it I tried using ternary operators:
p (i%3<1 || i.to_s.include?('3')) ? ((i%7<1 || i.to_s.include?('7')) ? "Fizzbuzz" : "Fizz") : ((i%7<1 || i.to_s.include?('7')) ? "Buzz" : i)
but when asked to solve it using the Enumerable methods(select,reject,collect etc) I was well stumped...Any body tried this before??
The select/collect methods were specificaly mentioned so I'm guessing that he had something like this in mind(excuse the crappy code)
(1..100).select { |i| i % 3 == 0 }.collect { "fizz" }
but i'm stuck when trying to do this for the 3 conditions and print out the result(ie iterate through the output array) :\
Probably not much help now, but I've produced a gem (fizzbuzzard) that monkey patches Fixnum such that all multiples of three print as Fizz, all multiples of five etc etc. Interviewers will respect you for using your knowledge of existing libraries rather than pointlessly re-solving solved problems.
Get it from rubgems here and find the source here.
The part I'm proudest of? The test suite. Output reproduced here:
(0h8m|master) [fizzbuzzard] $ rspec
.
Finished in 0 seconds
1 example, FIZZBUZZ failures
def fizzbuzz(i)
[nil,nil,"Fizz",nil, nil,nil,"Buzz"].each_with_index do |fizz_or_buzz, idx|
print fizz_or_buzz if (i.to_i % (1+idx)).zero?
end
end
If the interviewer doesn't laugh, then it's probably not going to be a good fit (for me anyways).
Note that print doesn't add a newline (vs p)
See http://rosettacode.org/wiki/FizzBuzz#Ruby for more common solutions.
This one's a little dense but fun. It abuses arrays and some functional methods slightly to avoid using any if statements.
rules = {
3 => ["fizz"],
7 => ["buzz"]
}
(1..100).map do |n|
[rules.map do |key, value|
value[n % key]
end.compact.join, n.to_s].find do |word|
!word.empty?
end
end.join("\n")
It is not clean to repeat the same conditionals.
p case [
i.%(3).zero? || i.to_s.include?("3"),
i.%(7).zero? || i.to_s.include?("7")
]
when [true, true] then "Fizzbuzz"
when [true, false] then "Fizz"
when [false, true] then "Buzz"
when [false, false] then i
end
This hands you back the results in an array of strings, you can do whatever you want with them at that point.
results = (1..100).to_a.collect do |i|
f = ((i%3).zero? || i.to_s.include?('3'))
b = ((i%7).zero? || i.to_s.include?('7'))
"#{:fizz if f}#{:buzz if b}#{i unless f || b}"
end
p results

Ruby: enumerator can't be coerced to Fixnum; struggling with Project Euler #5

The challenge is to find the smallest integer foo for which:
foo % 1..20 == 0
My current attempt is brute force
until foo % 1.upto(20) == 0 do
foo++
end
This outputs the error unexpected keyword end. But if I don't put the end keyword into irb the code never runs, because the block isn't closed.
I made an empty test case to see where my error lays
until foo % 1.upto(20) == 0 do
end
This throws a new error: enumerator can't be coerced to a fixnum. I imagine this means you can't directly perform modulus upon a range and expect a neat boolean result for the whole range. But I don't know where to go from here.
My first attempts forewent brute force in favor of an attempt at something more efficient/elegant/to-the-point and were as follows:
foo = 1
1.upto(20) {|bar| foo *= bar unless foo % i == 0}
gave the wrong answer. I don't understand why, but I'm also interested in why
foo = 1
20.downto(1) {|bar| foo *= bar unless foo % i == 0}
outputs a different answer.
EDIT: I would have used for loops (I got my feet wet with programming in ActionScript) but they do not work how I expect in ruby.
Your first solution is wrong because 1.upto(20) is an enumerator, that is, essentially an iterator over the values 1 to 20, and it cannot be used as a number for modulo or comparison to another number, since it itself isn't a number.
You really need two "loops" here:
foo = 1
foo += 1 until (1..20).all? { |i| foo % i == 0 }
the first loop is the until, and then the all? is another loop of sorts, in that it ensures that the block ({ |i| foo % i == 0 }) is true for each element in the range it is called on ((1..20)). Note that I'm using the one-line "backwards" syntax (which also works for if, unless, while, …)—the above is equivalent to:
foo = 1
until (1..20).all? { |i| foo % i == 0 } do
foo += 1
end
# foo => 232792560
Also, this is incredibly inefficient, Project Euler often involves a bit more math than programming, and a non-brute-force solution will likely involve more math but be far faster.
Try this:
until 1.upto(20).reject{|i| foo % i == 0 }.empty? do
foo += 1
end
I know it's not directly the OP question, but this is way easier to achieve with just:
puts (1..20).reduce(:lcm)
It's so simple that it seems like isn't fair to solve it this way, but that's precisely why Ruby is my language of choice for Project Euler.
See also this question
If this were me, I'd define a function to test the condition:
def mod_test(num)
test = (1..20).map {|i| num % i == 0}
test.all? # all are true
end
and then a loop to try different values:
foo = 20
until mod_test(foo) do
foo += 20
end
(Thanks to Dylan for the += 20 speedup.)
I'm sure that there's a clever way to use the knowledge of foo % 10 == 0 to also imply that foo % 5 == 0 and foo % 2 == 0, and perform only tests on prime numbers between 1 and 20, and probably even use that information to construct the number directly -- but my code ran quickly enough.

Resources