Division: Ruby no method error - ruby

I am trying to run this:
def ArithGeo(arr)
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)
It comes back with line 2 having an "undefined method" when I run this in terminal.
Why? The array is all numbers when testing with [1,2,3,100]. (And yes I know that this will return "Arithmetic" when it isn't. I haven't gotten to that part yet.)
Also, is to_i necessary? If items in an array are already considered an integer, they're an integer, right? I also tried with to_i on each array item but it returned a "division by zero" error even when none of the items in the array were 0, and I wasn't using position 0.

One (or more) of the elements in your arr is a String.
irb(main):009:0> "string"/"another string"
NoMethodError: undefined method `/' for "string":String
When you call to_i on a String it becomes 0.
irb(main):013:0* "string".to_i
=> 0
When you divide by 0 you get an error because you can't do that.
irb(main):011:0> "string".to_i/"another string".to_i
ZeroDivisionError: divided by 0
You can fix your code by changing this line:
print ArithGeo(STDIN.gets)
to this:
print ArithGeo(STDIN.gets.strip.split(',').map(&:to_i))
Then enter your inputs like this:
1,2,3,100 # don't include the "[]" around the numbers

Since your input is of ruby syntax [1,2,3,100] you need to evaluate it.
def ArithGeo(arr)
puts "#{arr.class} , #{arr}"
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
puts ArithGeo(eval STDIN.gets )
The input:
[1, 2, 3, 100]
The result:
Array , [1, 2, 3, 100]
Arithmetic
Also , I would recommend using floats to prevent integer rounding.
if arr[2].to_f/arr[1] == arr[3].to_f/arr[2]
Edit:
A much better (safer + more generic) is to use:
JSON.parse( array_string )
For example:
JSON.parse("[1 , 2]")
=> [1, 2]
JSON.parse("[1 , 2]").class
=> Array
And if you really want to be on the safe side , you'll need to add exception handling for JSON parsing errors.

You're passing a String to your method (IO.gets returns a string) when what you really want is an array of integers. If you just pass in this string, you will find that division is not defined for Strings. If you attempt to convert the input to an integer first, any leading non-numeric characters will cause the string to be converted to 0.
Try
arr = STDIN.gets.split(', ').map(&:to_i)
ArithGeo(arr)

It depends on your arr elements. Say, if arr elements are strings, then you will get a undefined method '/' for String (NoMethodError).
You need to make sure your arr elements are numbers i.e. integer or floats etc. on which the division (/) method is implemented.
Update
You can input the values comma separated and convert that string to an array using String#split method as I mentioned in the comment:
def ArithGeo(str)
arr = str.split(',').map(&:to_i) # split the values by comma and make them integer and put in array arr
# then you can use the following beause now your arr is an array, make sure you have at least 4 elements as you
# used index 3 in the calculation
if arr[2]/arr[1] == arr[3]/arr[2]
return "Geometric"
else
return "Arithmetic"
end
end
print ArithGeo(STDIN.gets)

Related

Optimize print output where i use check on zero. Ruby

Currently, I'm having print like this
print ((stamp_amount[0], 'first mark') unless stamp_amount[0].zero?), (', ' if !stamp_amount[0].zero? && !stamp_amount[1].zero?),
((stamp_amount[1], 'second mark') unless stamp_amount[1].zero?)
stamp_amount is an array with 2 integer values
Let's say in the current situation stamp_amount[0] = 10 and stamp_amount[1] = 3
Output preview:
10 first mark, 3 second mark
So if stamp_amount[0] = 0 the 10 first mark, part won't be show. Same if stamp_amount[1] = 0 the , 3 second mark part won't be shown
For me, it seems a little bit incorrect in terms of theory. Could you please suggest me the more correct or less painful print of this? :)
Cheers!
Your code is trying to join a sequence of up to two elements with a separator. The joining is a solved problem, see Array#join.
The problem can be then reduced to "how can I produce the correct sequence, given my stamp_amount input". Now this can be done in a thousand ways. Here's one:
def my_print(stamp_amount)
ary = [
!stamp_amount[0].zero? && stamp_amount[0],
!stamp_amount[1].zero? && stamp_amount[1],
].select{|elem| elem }
ary.join(', ')
end
my_print([10, 3]) # => "10, 3"
my_print([0, 3]) # => "3"
my_print([10, 0]) # => "10"
my_print([0, 0]) # => ""
Here's another
ary = []
ary << stamp_amount[0] unless stamp_amount[0].zero?
ary << stamp_amount[1] unless stamp_amount[1].zero?
ary.join(', ')
Here's yet another. This version can handle stamp_amount of any length.
ary = stamp_amount.reject(&:zero?)
ary.join(', ')
I'd go with the third, but the second one may be the easiest to understand for a beginner.
Use the select, as an alternative to reject (shown in part 3 of the answer by Sergio Tulentsev). It is just asa readable, and depending on the context and on the future changes to the code, you may prefer one versus the other.
puts stamp_amount.select{ |a| !a.zero? }.join(", ")
A few examples of inputs and outputs are:
stamp_amount output
--------------------------------------------------------------------------
10, 3 10, 3
10, 0 10
0, 3 3
0, 0 (prints an empty line, because the selected array is empty)
You're calculating zero? on index points more often than is needed, but the first thing I would look at refactoring here is the readability of the code. It might be nicer to calculate the message to print outside of the print method and explain what is happening with variable names.
# rubocop is going to complain about variable assignment like this
first_amount, second_amount = *stamp_amount
We can actually use the reason rubocop prefers the .zero? over == 0 or .empty? method to guide our development. zero? is in essence just empty? but it communicates the meaning of what you are attempting to do in a better manner. I would use this reasoning when assigning strings to variables that explain what they are doing.
some_name_that_explains_what_this_is_0 = "#{first_amount} piecu centu marka"
some_name_that_explains_what_this_is_1 = "#{second_amount} tris centu marka"
Your current code is confusing as you have the possibility of printing a string like "10 tris centu marka" which does not make lexical sense and probably not what you are after considering tis evaluates to 'second mark', which would pose an issue if the first value is zero. We also could reject zero integers before we start converting them to strings.
array = [1, 0].reject(&:zero?)
Now we can take the array and do something like:
string = []
array.each_with_index { |e, i| string << "#{e} #{Ordinalize.new(i).ordinalize} mark" }
message = string.join(', ')
print(message)
# ord class
class Ordinalize
def initialize(value)
#value = value
end
def ordinalize
mapping[#value]
end
def mapping
# acounting for zero index
['first', 'second']
end
end
where we are calculating the ordinalization and letting our new class handle the sentence structure for us.
Outputs:
[1, 0] => "1 first mark"
[0, 1] => "1 first mark"
[1, 2] => "1 first mark, 2 second mark"

Converting to_i doesn't work (Ruby)

I'm pretty new to programming and i'm doing fundamentals on codewars.com and I'm having some trouble with this one. The objective is to take a group of integers, reverse them, and put them into an array. Here's my code. (I made the tf function to see what was going on in the code.)
def digitize(n)
answer = n.to_s.split(//).reverse!
def tf(it)
str_test = it.is_a? String
int_test = it.is_a? Integer
puts "String: #{str_test}"
puts "Integer: #{int_test}"
end
Array(answer)
unless answer.is_a? Integer
for item in answer
item.to_i
puts item
tf(item)
end
end
return answer
end
Sample test:
Test.assert_equals(digitize(35231),[1,3,2,5,3])
When tested, it returns:
1
String: true
Integer: false
3
String: true
Integer: false
2
String: true
Integer: false
5
String: true
Integer: false
3
String: true
Integer: false
Can one of you guys help me figure out where it goes wrong?
Assigning
item = item.to_i
Would fix the output in tf, but your returned answer would still be all strings. If you want to do this one by one like you're doing you would need to assign it back into the index of the array:
answer.each_with_index do |item, index|
answer[index] = item.to_i
end
Though, an even better way to do this would be with map (returns a new array) or map! (in-place):
# return this line (make it the last line in the method) or make sure
# to re-assign answer
answer.map(&:to_i)
# or do this one to use `answer` later on with all integers.
answer.map!(&:to_i)
(See this question about that &:to_i syntax).
It should also be noted (maybe), that Rubyists in general don't like for loops and prefer each loops.
Also, the line:
Array(answer)
doesn't modify the answer in place, and returns it cast to an array, so the line is doing nothing:
a = "1"
Array(a) # => ["1"]
a # => "1"
a = Array(a) # => ["1"]
a # => ["1"]
You also, don't even need to do this, since answer is already an array from where you split it (You could also have used chars instead of split(//)). The line unless answer.is_a?(Integer) will thusly never be true.
The last major thing, I see is that in newer versions of ruby, there's a built-in method to do all this, digits:
35231.digits # => [1, 3, 2, 5, 3]

Accidental Type conversion in Ruby with strings

I'm trying to solve a challenge where you take in a string of words but return the longest word in the string. My strategy is to break the string into an array of individual words and search the array. However, I'm getting a type conversion error. What is causing the type conversion error? This is particularly strange to me because I don't actually see any type conversion happening here.
def LongestWord(sen)
sen1 = sen.split("/\W+/")
grt = 0
sen1.each do |i|
if sen1[i].length > sen1[grt].length # Type conversion error
grt = i
end
end
sen1[grt]
end
# keep this function call here
puts LongestWord(STDIN.gets)
The type conversion is caused by the array entry i being converted (probably unsuccessfully) into an integer (though I suppose it could be ruby trying to convert the array into a hash, and use i as a key to the hash).
Your misunderstanding is that you think you're getting the array's indices passed into the block for each. What is passed in to that block is each individual value in the array. I.e., if your string sen is 'this is a silly string', then the values passed are 'this', 'is', 'a', 'silly', and 'string'.
You get the error because, when the code is running, i is the first value of sen1, which results in sen1['some string'] being evaluated.
An array can't have a string index, only a hash can, resulting in the Type error.
Meditate on this:
def longest_word(sen)
sen1 = sen.split # => ["foo", "barbaz"]
grt = 0
sen1.each do |i|
i # => "foo"
sen1 # => ["foo", "barbaz"]
sen1[i] # =>
sen1[grt] # =>
sen1[i].length # =>
sen1[grt].length # =>
if sen1[i].length > sen1[grt].length #Type conversion error
grt = i # =>
end
end
sen1[grt]
end
# keep this function call here
longest_word('foo barbaz')
Breaking it down further, here's the offending problem:
sen1 = 'foo barbaz'.split
sen1['foo'] # =>
# ~> TypeError
# ~> no implicit conversion of String into Integer
You don't see the type conversion, but it is there. In more than one place.
As Derrell Durrett pointed out in his answer, your are assuming (wrongly) that the index of the array is passed to the block, not its elements.
Then you write if sen1[i].length > sen1[grt].length. Let's consider the string is 'this is a silly string'. The first element is 'this' and what you are trying to do is if sen1['this'].length > sen1[0].length. As Ruby arrays always have integer indexes, Ruby tries to convert 'this' to an integer in order to find the element at the specified position. Of course this fails!
But your code is not that far from being right. A few small changes and it will run perfectly well:
def longest_word(sen)
sen1 = sen.split(" ")
grt = 0
sen1.each_index do |i|
if sen1[i].length > sen1[grt].length
grt = i
end
end
sen1[grt]
end
puts LongestWord(STDIN.gets)
Now you'd be passing the indexes with sen1.each_index and it'd be working fine.
Notice that I changed the name of your method to longest_word. This is much better, in fact, because this first capital letter is reserved to constants and class names.
I also would like to point that you are not using a good Ruby style. This could be written like this:
def longest_word(str)
str.split(" ").max_by{ |s| s.length }
end
and the result would be the same.

Iterate over array of arrays

This has been asked before, but I can't find an answer that works. I have the following code:
[[13,14,16,11],[22,23]].each do |key,value|
puts key
end
It should in theory print:
0
1
But instead it prints:
13
22
Why does ruby behave this way?
Why does ruby behave this way?
It's because what actually happens internally, when each and other iterators are used with a block instead of a lambda, is actually closer to this:
do |key, value, *rest|
puts key
end
Consider this code to illustrate:
p = proc do |key,value|
puts key
end
l = lambda do |key,value|
puts key
end
Using the above, the following will set (key, value) to (13, 14) and (22, 23) respectively, and the above-mentioned *rest as [16, 11] in the first case (with rest getting discarded):
[[13,14,16,11],[22,23]].each(&p)
In contrast, the following will spit an argument error, because the lambda (which is similar to a block except when it comes to arity considerations) will receive the full array as an argument (without any *rest as above, since the number of arguments is strictly enforced):
[[13,14,16,11],[22,23]].each(&l) # wrong number of arguments (1 for 2)
To get the index in your case, you'll want each_with_index as highlighted in the other answers.
Related discussions:
Proc.arity vs Lambda.arity
Why does Hash#select and Hash#reject pass a key to a unary block?
You can get what you want with Array's each_index' method which returns the index of the element instead of the element itself. See [Ruby'sArray` documentation]1 for more information.
When you do:
[[13,14,16,11],[22,23]].each do |key,value|
before the first iteration is done it makes an assignment:
key, value = [13,14,16,11]
Such an assignment will result with key being 13 and value being 14. Instead you should use each_with_index do |array, index|. This will change the assignment to:
array, index = [[13,14,16,11], 0]
Which will result with array being [13,14,16,11] and index being 0
You have an array of arrays - known as a two-dimensional array.
In your loop, your "value" variable is assigned to the first array, [13,14,16,11]
When you attempt to puts the "value" variable, it only returns the first element, 13.
Try changing puts value to puts value.to_s which will convert the array to a string.
If you want every value, then add another loop block to your code, to loop through each element within the "value" variable.
[[1,2,3],['a','b','c']].each do |key,value|
value.each do |key2,value2|
puts value2
end
end

In Ruby irb, can't access a number in a array like fashion

I tried this in irb:
x = 123456
Then I wanted to get a specific position of the number like:
puts x[2]
it returns 0
why is that?
The only (sensible) way to do this is to first convert it to a string then use the [] method:
x_str = x.to_s
puts x_str[0..2] #prints "12"
If you want to retrieve the position of a string within another string, use the index method
puts x_str.index('2') #prints 1
Fixnum does supply a [] method, but it's obviously not what you want.
In your code, it's returning 0 because that is the 3rd (zero-indexed) bit in the binary representation of 123456.

Resources