Ruby Loop Countdown Method keeps returning "nil" - ruby-on-rails

I am working on a Ruby challenge for work, and I am unable to create a working method. Every method I try keeps returning "nil".
Here is the question:
Create a method that passes an integer argument to a single parameter. If the integer is greater than 0 print the numbers from the integer to 0. If the number is less than 0 simply print the integer. Use a for loop, while loop, or unless loop to print the range of numbers from the integer to 0.
For example:
sample(4)
output = 3, 2, 1
sample(-1)
output = -1
Here is the code I tried to use
def countdown(n)
loop do
n -= 1
print "#{n}"
break if n <= 0
end
countdown(4)

A method returns the results of the last statement executed. Your loop is returning nil:
def countdown(n)
x = loop do
n -= 1
puts "#{n}"
break if n <= 0
end
x
end
countdown(4)
3
2
1
0
=> nil
Now let's return something:
def countdown(n)
loop do
puts "#{n}"
break if n <= 0
n -= 1
end
"okay we're done"
end
countdown(4)
4
3
2
1
0
=> "okay we're done"

It's not necessary to print inside the function and also outside it - this will cause duplicate printing. Also you are calling print on the positive numbers but not calling print if they are negative or zero. Additionally, you are using print "#{n}" which is the same as print n.
As far as the title of your question goes - "keeps returning nil" - you can change your approach a bit to do the print calls outside the function.
def countdown(n)
n <= 1 ? [n] : (n-1).downto(1).to_a
end
print countdown(n).join(", ")

Try this:
def countdown(n)
n.downto(n > 0 ? 0 : n) { |i| puts i }
end
countdown(4)
# 4
# 3
# 2
# 1
# 0
countdown(-4)
# -4
countdown(0)
# 0
You didn't mention what is to be done if the argument is zero. I've assumed it's treated as a positive or negative number.
Admittedly, this is cheating, as it does not "Use a for loop, while loop, or unless loop...", but Ruby is designed mainly to use iterators and blocks. This, or something like it, is the way to go. I just had a thought: treat that as a suggestion, not a requirement.
By the way, among loops, Kernel#loop was not mentioned, which is strange, as it is quite useful. As for "for loops", who uses them? I never have, not even once.
If you must use a loop, you could do the following.
def countdown(n)
while n > 0
puts n
n-= 1
end
puts n
end
countdown(4)
# 4
# 3
# 2
# 1
# 0
countdown(-4)
# -4
countdown(0)
# 0

You may try this...
def sample (a)
if a > 0
(1..a).to_a.reverse
else
a
end
end
Hope this will work for you

Related

in `+': nil can't be coerced into Integer (TypeError) Ruby on Rails

Get the following error when testing my method for false test cases shown below, not sure why. Other test cases that result in true work out well. The method takes in an array of integers and returns true if any three consecutive elements sum to 7 and returns false otherwise.
def lucky_sevens?(numbers)
i=0
while i <= numbers.length
if (numbers[i] + numbers[i+1] + numbers[i+2]) == 7
return true
end
i+=1
end
end
puts lucky_sevens?([7,7,7,7,]) == false
puts lucky_sevens?([3,4,3,4]) == false
You're checking up to the last element and then 2 number after it. Accessing an element in array bigger then the length returns nil. Trying to add nil to a number will produce the error you saw.
To solve it you should stop checking when the last of the 3 elements reaches the end of the array, not the first. Meaning you should stop 2 elements earlier.
It's easily achieved by just subtracting 2 from the length in your loop.
def lucky_sevens?(numbers)
i=0
while i < numbers.length - 2
if (numbers[i] + numbers[i+1] + numbers[i+2]) == 7
return true
end
i+=1
end
false
end
A more ruby way to do this, would be the following:
def lucky_sevens?(numbers)
numbers.each_cons(3).any? do |group_of_3|
group_of_3.sum == 7
end
end

How does Enumerator.new work with block passed?

I struggle a little bit with understanding how the Enumerator.new method works.
Assuming example from documentation:
fib = Enumerator.new do |y|
a = b = 1
loop do
y << a
a, b = b, a + b
end
end
p fib.take(10) # => [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Where's the loop break condition, how does it know how many times loop should to iterate (as it doesn't have any explicit break condition and looks like infinite loop) ?
Enumerator uses Fibers internally. Your example is equivalent to:
require 'fiber'
fiber = Fiber.new do
a = b = 1
loop do
Fiber.yield a
a, b = b, a + b
end
end
10.times.map { fiber.resume }
#=> [1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
Edit I think I understand your question now, I will still keep my original answer below.
y << a is an alias for y.yield(a), which is basically a sleep with return value. Each time a value is requested from the enumerator with next, the execution is continued until another value is yielded.
Enumerators do not need to enumerate a finite number of elements, so they are infinite. For example, fib.to_a will never terminate, because it tries to build an array with an infinite number of elements.
As such, enumerators are great as a representation of infinite series such as the natural numbers, or in your case, the fibonacci numbers. The user of the enumerator can decide how many values it needs, so in your example take(10) determines the break condition if you will.
The break condition itself is in the implementation of Enumerator#take. For demonstration purposes, we can make our own implementation called my_take:
class Enumerator
def my_take(n)
result = []
n.times do
result << self.next
end
result
end
end
Where you could of course "mentally substitute" your n.times loop with your classical C style for (i=0; i<n; i++). There's your break condition. self.next is the method to get the next value of the enumerator, which you can also use outside of the class:
fib.next
#=> 1
fib.next
#=> 1
fib.next
#=> 2
fib.next
#=> 3
That said, you can of course build an enumerator that enumerates a finite number of values, such as the natural numbers in a given range, but that's not the case here. Then, the Enumerator will raise a StopIteration error when you try to call next, but all values already have been enumerated. In that case, you have two break conditions, so to speak; the one that breaks earlier will then win. take actually handles that by rescuing from the error, so the following code is a bit closer to the real implementation (however, take is in fact implemented in C).
class Enumerator
def my_take(n)
result = []
n.times do
result << self.next
end
result
rescue StopIteration
# enumerator stopped early
result
end
end

Ruby custom sorting returns -1 +1 instead of array

I'm trying to implement my first ruby sorting algorithm. This algorithm is based on some specific rules ("always prefer objects of type xxx over objects of types yyy"), and if none of these rules triggered, it uses the ruby <=>-operator. I'm doing this on a ruby-on-rails one-to-many association.
The problem is this algortihm does not return the array itself, it just returns -1 or 1, the result of the comparison..But I actually don't understand why, as my result is only returned in the sort-block.
Here is my current code:
def sort_products!
products.sort! do |p1, p2|
result = 0
# Scalable Products are always the last ones in order
if p1.class.name == "ScalableProduct"
result = -1
elsif p2.class.name == "ScalableProduct"
result = 1
end
if result == 0
# Put products producing electricity and heating down
if p1.can_deliver_electricity?
result = -1
elsif p2.can_deliver_electricity?
result = 1
end
end
# Else: Just compare names
result = p1.name <=> p2.name if result == 0
result
end
end
The best practice here, in my opinion, would be to implement the <=> inside the Product model. You'll need to include the Comparable model in order to achive this:
class Product
include Comparable
def <=>(another_product)
# Compare self with another_product
# Return -1, 0, or 1
end
end
Then your sorting method will be reduced to:
def sort_products!
products.sort!
end
Change the do..end for brackets as delimiters of the block. It is first sorting, and then using the block on the result (because of the precedence of the do..end syntax). Using brackets, it uses the block as a sorting block, which is what you wanted.
Also, in your comparison, if both your products are ScalableProduct then you will not order them in a sensible way. If they are both ScalableProduct at the same time, you might want to keep result as 0 so it falls back to comparing by name. Same deal with can_deliver_electricity?.

Ruby exits my each loop when an if-statement inside is false

Why does code below exit the each loop as soon as the if-statement "g2k.has_key?(k)" is false
e.element_children.all? do |n|
k = n.name.to_sym
logger.info("#{n.name} as symbol is '#{k}' is valid? = #{g2k.has_key?(k)}")
if g2k.has_key?(k)
logger.info("#{g2k[k] }= #{n.content}")
# #vehicle_data[g2k[k]] = n.content
end
end
This loops through all element children as intended
e.element_children.all? do |n|
k = n.name.to_sym
logger.info("#{n.name} as symbol is '#{k}' is valid? = #{g2k.has_key?(k)}")
#if g2k.has_key?(k)
# logger.info("#{g2k[k] }= #{n.content}")
# #vehicle_data[g2k[k]] = n.content
#end
end
I'm using rails 3.2 with Ruy 1.9, parsing XML with nokogiri.
Once all? finds something false, then it can't be all, so it's going to stop processing.
Here's an example: http://rubyfiddle.com/riddles/cb777 --- you'll see that it stops after printing 5 because 5 is not < 5
(1..20).all? do |i|
puts i
i < 5
end
# prints:
1
2
3
4
5
Unless you are capturing the output of the .all? method you should not be using it. .all? is used to make sure every element in an array returns true. .each will just iterate over all of them. .detect will iterate until true is returned.

Executing code if three conditions are false

I'm trying to skip over calculating some numbers when the result would be an attempt to insert NaN into the DB. My code is as follows:
unless #X = 0 || #Y = 0 || Z= 0 #Don't execute below code if any of the three values = 0
#Do some stuff with #X, #Y and #Z
end
I know that X,Y and Z are positive integers, as they should be, however this statement is not triggering the code block in the unless clause. Am I blatantly misusing the || operator?
You're using = the assignment operator. You want to be using == the equality operator. Your code should look like this:
unless #X == 0 || #Y == 0 || #Z == 0
...
end
You should be using a double equals (==) for comparison in an if or unless clause, not a single equals (=).
Especially when you want to compare with zero, there is a built in command in ruby which is faster than doing == 0.
unless #x.zero? or #y.zero? or #z.zero?
...
end
You can use either || or or here.

Resources