Variable and expressions in ruby - ruby

I am trying to create a ruby program where three numbers are entered and their sum is taken but if any numbers are the same they don't count toward the sum.
example (4,5,4) = 5
My problem is with my expressions. If i enter the same number I get multiple output for various combination. example enter 5,5,5 = 15,5,0
if a != b or c then
puts a+b+c
elsif b != a or c then
puts a+b+c
elsif c != a or b then
puts a+b+c
end
if a == b then
puts c
elsif a == c then
puts b
elsif b == c then
puts a
end
if a == b and c then
puts 0
elsif b == a and c then
puts 0
elsif c == a and b then
puts 0
end

Solving it with two beautiful self explanatory one-liners
array = [a,b,c]
array = array.keep_if {|item| array.count(item) == 1 }
array.inject(0){|sum,item| sum + item}
-The first line creates an array with your parameters.
-The second line only keep the items whose count equals to 1 (remove the ones that appear more than one time), and store that on the array.
-The third line sums all the remaining elements.
Voilà, the ruby way :)

Related

Iterate through alphabet in Ruby until X

When using input x, I'm trying to iterate through the alphabet through to that point so, if I put in 44, I'll iterate to 18 from this method.
I can see a lot of methods on SO for iteration a..z, a..zzz, etc, but less for iteration to position x and outputting the associated letters. Is there a ruby method for flipping an input letter to a number within a dynamic range?
def get_num(x)
pos = x%26
(1..pos).each do |c|
puts c
#outputs letter for position c
# end
end
get_num(44) # => Expected: 44%26 = 18; iterate 1 to 18 (pos) to get A..R list as output.
Using the #Integer.chr method, 'a'..'z' == 97..122, and 'A'..'Z' == 65..90 That means:
def get_num(x)
pos = x%26
(96+pos).chr
end
get_num(44)
#=> "r"
OR
def get_num(x)
pos = x%26
(64+pos).chr
end
get_num(44)
#=> "R"
So, to complete your method:
def get_num(x)
pos = x%26
(1..pos).each do |c|
puts (c+64).chr
end
end
get_num(44)
#=>
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R

Identifying Triangle with if/else

Question is a user gives 3 sides and identifies triangles, like equilateral, isosceles and scalene. Here is my coding, I don't know why gives any sides that always show up "invalid". I think it's logic wrong, but I can't figure out.
puts "please input the length of 3 sides:"
a = gets.chomp.to_i
b = gets.chomp.to_i
c = gets.chomp.to_i
if a + b <= c
puts "invalid"
elsif a <= 0 || b <= 0 || c <= 0
puts "invalid"
else
if a == b && b == c
puts"equilateral triangle"
elsif a == b
puts"isosceles triangle"
else
puts"scalene triangle"
end
end
The fact that your code always prints "invalid" makes me think that input is passed in on one line instead of being on separate lines. For example, when the input is:
50 50 50
instead of getting 50 in all three variables you would get 50 in a and 0 in b, c. This is because gets takes in an entire line instead of taking one value.
In such an event, this is what you need:
a, b, c = gets.split.map{ |value| value.to_i }
A better more effective way to do this is to store the values of the triangle sides into a hash first, the value of of each triangle side will be the keys, and the value of each key can be the repeats. This will work with strings too.
Here is an Example.
# First you get an array, you can use gets.chomp as string and split to array, whichever way you choose, but in the end we end up with an array, and we pass the array to the method.
def triangle_type(arr)
# Create new empty hash
repeated_sides = Hash.new(0)
# First make sure the array is only a length of three. (this is optional)
if arr.length == 3
# Iterate through each value in the array and store it to to a hash to find duplicates
arr.each do |x|
repeated_sides[x] += 1
end
# sort the hash by it's values in descending order, for logic to work later.
repeated_sides = repeated_sides.sort_by {|k,v| v}.reverse.to_h
# uncomment this below to see the duplicate sides hash
#puts "#{repeated_sides}"
# Iterate through the sorted hash, apply logic starting from highest and first value the iterator will find.
repeated_sides.each do |k,v|
return v == 3 ? 'Equilateral Triangle' : v == 2 ? 'Isosceles Triangle' : 'Scalene Triangle'
end
end
# Return Not a triangle if the condition fails
return 'Not a triangle'
end
# Test with integers
puts triangle_type([4,1,2,5]) # output: Not a triangle
puts triangle_type([3,3,3]) # output: Equilateral Triangle
puts triangle_type([4,3,3]) # output: Isosceles Triangle
puts triangle_type([4,2,3]) # output: Scalene Triangle
# Test with strings
puts triangle_type(['4','1','2','5']) # output: Not a triangle
puts triangle_type(['3','3','3']) # output: Equilateral Triangle
puts triangle_type(['4','3','3']) # output: Isosceles Triangle
puts triangle_type(['4','2','3']) # output: Scalene Triangle
puts triangle_type(['a','a','a']) # output: Equilateral Triangle
puts triangle_type(['a','c','c']) # output: Isosceles Triangle
puts triangle_type(['a','b','c']) # output: Scalene Triangle
Skipping user inputs, since I can not reproduce the error (even if Unihedron found a fix) there is still a problem with the logic.
When the input is a = 1000, b = 1, c = 1, the result is "scalene triangle", but it should return "invalid". Below a fix I suggest.
Let's store the input in an array (already converted into integer or float):
sides = [a, b, c]
First you need to check that all sides are positive:
sides.all? { |x| x > 0 }
Then, check that the sum of two sides is greater than the other:
sides.combination(2).map{ |x| x.sum }.zip(sides.reverse).all? { |xy, z| xy > z }
Finally (I'm missing something?), to pick the triangle denomination you can use an hash accessing it by sides.uniq result:
triangle_kinds = {1 => 'equilateral', 2 => 'isosceles', 3 => 'scalene'}
triangle_kinds[sides.uniq.size]
Used the following methods over array (enumerable):
https://ruby-doc.org/core-2.5.1/Enumerable.html#method-i-all-3F
https://ruby-doc.org/core-2.5.1/Array.html#method-i-combination
https://ruby-doc.org/core-2.5.1/Array.html#method-i-map
https://ruby-doc.org/core-2.5.1/Array.html#method-i-zip
https://ruby-doc.org/core-2.5.1/Array.html#method-i-reverse
https://ruby-doc.org/core-2.5.1/Array.html#method-i-sum
https://ruby-doc.org/core-2.5.1/Array.html#method-i-uniq

Comma assignment (why right go first?) i.e. n,m = 1, 2?

In the process of figuring out blocks and yields I came across these comma-separated assignments:
def fibit
n,m =1,1
loop do |variable|
yield n
n,m = m,n+m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end
Why does the m get assigned first in this scenario?
Does the last one always go first? If so why?
This was also seen as only e has the value of 1 meaning e went first?
e,r=1
puts r
puts "-------"
puts e
Also, does anyone have an idea why the code-block versions just executes, where if I write the same code with no code block I actually need to call the method for it to run?
def fibit
n,m =1,1
loop do |variable|
puts "Next : #{n}"
break if n > 100
n,m = m,n+m
end
end
fibit
If I didn't have the last line it wouldn't run. Where in the first one I don't actually call the fibit method? Or is the block kicking it off?
m does not get assigned first. When using multiple assignments, all right hand side calculations are done before any assignment to the left hand side.
That's how this code works:
a = 1
b = 3
a, b = b, a
a
# => 3
b
# => 1
This would not be possible if the assignment was done serially, since you would get that both would be either equal 1 or 3.
To further prove my point, simply swap the assignment of n and m in your code, and you'll find that the result is the same:
def fibit
n,m =1,1
loop do |variable|
yield n
m,n = n+m,m # this line
puts "n is #{n} m is #{m}"
end
end
fibit do |num|
puts "Next : #{num}"
break if num > 100
end

Iterating through an array (Project Euler #23)

I have the following code
#!/usr/bin/ruby -w
c = 1
d = Array.new(6965) #6965 is the amount of abundant numbers below 28123 of which all numbers greater than that can be written as the sum of two abundant numbers
f = 0
while c < 28124 # no need to go beyond 28123 for this problem
a = 0
b = 1
i = true # this will be set to false if a number can be written as the sum of two abundant numbers
while b <= c/2 + 1 # checks will go until they reach just over half of a number
if c % b == 0 # checks for integer divisors
a += b # sums integer divisors
end
b += 1 # iterates to check for new divisor
end
if a > c # checks to see if sum of divisors is greater than the original number
d << c # if true it is read into an array
end
d.each{|j| # iterates through array
d.each{|k| # iterates through iterations to check all possible sums for number
# false is declared if a match is found. does ruby have and exit statement i could use here?
i = false if c - j - k == 0
}
}
c+=1 # number that we are checking is increased by one
# if a number cannot be found as the sum of two abundant number it is summed into f
f += c if i == true
end
puts f
For the following code, whenever I try to do a double iteration for my d array, I come up with the following errors:
euler23:21:in -': nil can't be coerced into Fixnum (TypeError)
from euler23:21:inblock (2 levels) in '
from euler23:20:in each'
from euler23:20:inblock in '
from euler23:19:in each'
from euler23:19:in'
As I'm not familiar with Ruby, my various attempts at resolving this have been for naught. I get the feeling that there are some libraries I need to include, but my research hasn't mentioned any libraries, and I am at a loss. This code is meant to sum all the numbers that cannot be written as the sum of two abundant numbers; it is the twenty third question from Project Euler.
When you do this:
d = Array.new(6965)
you create an array of 6965 nil values.
If before line 21 you add this test code:
p [c,j,k]
then you get the result:
[1, nil, nil]
which shows that j and k are both nil values. You are iterating through empty items in your array.
If you change your creation of d to just:
d = [] # an empty array, which in Ruby can change size whenever you want
...then your code runs. (I've not let it run long enough to see if it runs correctly, but it runs without error for quite some time at least.)
Finally, a few bits of random style advice:
This code:
while b <= c/2 + 1
if c % b == 0
a += b
end
b += 1
end
can be rewritten more concisely and more Ruby-esque as:
b.upto(c/2+1){ a+=b if c%b==0 }
Similarly, this loop:
c=1
while c < 28124
# ...
c += 1
end
can be rewritten as:
1.upto(28123) do |c|
# ...
end
When you ask about breaking out of a loop, you can use break or next as appropriate, or throw and catch—which is NOT used for error handling in Ruby— to jump to a particular nested loop level.
The code below is faulty:
d.each{|j|
d.each{ |k|
p c,j,k #1,nil,nil
i = false if c - j - k == 0 }}
Because of:
1 - nil - nil
#TypeError: nil can't be coerced into Fixnum
# from (irb):2:in `-'
# from (irb):2
# from C:/Ruby193/bin/irb:12:in `<main>'

Two indexes in Ruby for loop

can you have a ruby for loop that has two indexes?
ie:
for i,j in 0..100
do something
end
Can't find anything in google
EDIT: Adding in more details
I need to compare two different arrays like such
Index: Array1: Array2:
0 a a
1 a b
2 a b
3 a b
4 b b
5 c b
6 d b
7 d b
8 e c
9 e d
10 e d
11 e
12 e
But knowing that they both have the same items (abcde)
This is my logic in pseudo, lets assume this whole thing is inside a loop
#tese two if states are for handling end-of-array cases
If Array1[index_a1] == nil
Errors += Array1[index_a1-1]
break
If Array2[index_a1] == nil
Errors += Array2[index_a2-1]
break
#this is for handling mismach
If Array1[index_a1] != Array2[index_a2]
Errors += Array1[index_a1-1] #of course, first entry of array will always be same
if Array1[index_a1] != Array1[index_a1 - 1]
index_a2++ until Array1[index_a1] == Array2[index_a2]
index_a2 -=1 (these two lines are for the loop's sake in next iteration)
index_a1 -=1
if Array2[index_a2] != Array2[index_a2 - 1]
index_a1++ until Array1[index_a1] == Array2[index_a2]
index_a2 -=1 (these two lines are for the loop's sake in next iteration)
index_a1 -=1
In a nutshell, in the example above,
Errors looks like this
a,b,e
As c and d are good.
You could iterate over two arrays using Enumerators instead of numerical indices. This example iterates over a1 and a2 simultaneously, echoing the first word in a2 that starts with the corresponding letter in a1, skipping duplicates in a2:
a1 = ["a", "b", "c", "d"]
a2 = ["apple", "angst", "banana", "clipper", "crazy", "dizzy"]
e2 = a2.each
a1.each do |letter|
puts e2.next
e2.next while e2.peek.start_with?(letter) rescue nil
end
(It assumes all letters in a1 have at least one word in a2 and that both are sorted -- but you get the idea.)
The for loop is not the best way to approach iterating over an array in Ruby. With the clarification of your question, I think you have a few possibly strategies.
You have two arrays, a and b.
If both arrays are the same length:
a.each_index do |index|
if a[index] == b[index]
do something
else
do something else
end
end
This also works if A is shorter than B.
If you don't know which one is shorter, you could write something like:
controlArray = a.length < b.length ? a : b to assign the controlArray, the use controlArray.each_index. Or you could use (0..[a.length, b.length].min).each{|index| ...} to accomplish the same thing.
Looking over your edit to your question, I think I can rephrase it like this: given an array with duplicates, how can I obtain a count of each item in each array and compare the counts? In your case, I think the easiest way to do that would be like this:
a = [:a,:a,:a,:b,:b,:c,:c,:d,:e,:e,:e]
b = [:a,:a,:b,:b,:b,:c,:c,:c,:d,:e,:e,:e]
not_alike = []
a.uniq.each{|value| not_alike << value if a.count(value) != b.count(value)}
not_alike
Running that code gives me [:a,:b,:c].
If it is possible that a does not contain every symbol, then you will need to have an array which just contains the symbols and use that instead of a.uniq, and another and statement in the conditional could deal with nil or 0 counts.
the two arrays are praticatly the same except for a few elements that i have to skip in either/or every once in a while
Instead of skipping during iterating, could you pre-select the non-skippable ones?
a.select{ ... }.zip( b.select{ ... } ).each do |a1,b1|
# a1 is an entry from a's subset
# b1 is the paired entry bfrom b's subset
end

Resources