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

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.

Related

After Rails Proc turns a block into an object, why can a class call it?

I got a sample code that turns a block into an object using Proc.new so that it can be executed independently. There are two puts at the end to print the result, in this example, the array is brought into the block for execution. At this point, I have something that I don't understand.
In the following code, why is .iterate!(square) valid? They are the method defined in the class and the name of a separate block object. Why can they be written together?
Second, what is the working process of def iterate!(code)? Why is the value being carried into (code)? And what are self and code.call here?
square = Proc.new do |n|
n ** 2
end
class Array
def iterate!(code)
self.map {|n| code.call(n)}
end
end
puts [1, 2, 3].iterate!(square)
puts [4, 5, 6].iterate!(square)
I'm a beginner, please explain as detailed as possible, thank you.
square = Proc.new do |n|
n ** 2
end
This is a simple proc, which expect an integer argument, and return square of the argument.
eg:
square.call(5) => 25
square.(5) => 25
Now, You have opened Array class and added iterate! method which takes an argument. and the method just work on self (self here refers to same array on which you are calling the iterate method. here self = [1,2,3] in your first case and [4,5,6] in you second case.
in the iterate! method definition, you are mapping/looping the array elements and you are calling the square proc with each element as arguments.
So it is more like
square.call(1) => 1
square.call(2) => 4
square.call(3) => 9

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

Ruby Loop Countdown Method keeps returning "nil"

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

Ruby undefined method `id' for nil:NilClass or Loop error

...
begin
last = Folder.where(name: #folders.last, user_id: #user_id)
prev = Folder.where(name: #folders[count], user_id: #user_id)
for z in 0..last.count
for x in 0..prev.count
valid = Folder.exists?(name: last[z].name, parent_id: prev[x].id)
case valid
when true
#test += valid.to_s
#ids << Folder.find_by(id: prev[x].id).id
##ids = #ids[0].id
else
end
end
end
#test += 'MSG'
rescue Exception => e
#test = e.message
valid = false
else
end
This is a portion of code, everything working fine except the code after loops which displays message #test += 'MSG'. There is an exception in rescue block, which says undefined method `id' for nil:NilClass but the method returns the id, so its working. What is the issue, please help? Why the code after two loops will not working
The loop should be 0..(last.count-1) and 0..(prev.count-1), to account for 0 index.
Or a more readable excluded end range (as suggested by Neil Slater)
0...last.count and 0...prev.count
EDIT
Lets say last has 3 items in it. Then looping through 0..3 will go through
last[0], last[1], last[2], last[3] #(4 items) Which will result in error
So instead, you should loop through 0..2 or 0...3 (three dots means exclude last num)
Your problem is that your iterators x and z get too large and reference empty array indexes. But your code does not actually need them, as you only use x and z to index into the separate arrays.
It is quite rare in Ruby to use for loops to iterate through an Array. The core Array class has many methods that give ways to iterate through and process lists of objects, and it is usually possible to find one that does more precisely what you want, simplifying your code and improving readability.
Your code could be re-written using Array#each:
last = Folder.where(name: #folders.last, user_id: #user_id)
prev = Folder.where(name: #folders[count], user_id: #user_id)
last.each do |last_folder|
prev.each do |prev_folder|
valid = Folder.exists?(name: last_folder.name, parent_id: prev_folder.id)
case valid
when true
#test += valid.to_s
#ids << Folder.find_by(id: prev_folder.id).id
else
end
end
end
#test += 'MSG'
... etc

counting regexp matches within the method

I have some code like below. comment method is called whenever some comment occurs in the html. Then, I am doing a regexp match, I want to count the number of matches within the parsed comments. Its printing like below
1
2
3
4
5
what I want is to just print 5 because thats the total number of matches. can someone help pls.
class PlainTextExtractor < Nokogiri::XML::SAX::Document
def comment(string)
# I am defining some regexp here
m = Regexp.new(re, Regexp::IGNORECASE);
if m.match(string)
$count += 1
puts $count
end
end
end
parser = Nokogiri::HTML::SAX::Parser.new(PlainTextExtractor.new)
parser.parse_memory(html)
Just move your puts $count out of the loop. You can put it at the end, after you call the parser.
If you are only interested in the number of matches you can do
m = Regexp.new(re, Regexp::IGNORECASE);
puts string.scan(m).length
One way is to make your class count the number of matches internally in an instance variable, eg #count. Then use attr_reader to create a method allowing you to read its value at the end. Also you don't need a global variable. Example (not tested):
class PlainTextExtractor < Nokogiri::XML::SAX::Document
attr_reader :count
def comment(string)
# I am defining some regexp here
m = Regexp.new(re, Regexp::IGNORECASE);
if m.match(string)
#count += 1
end
end
end
pt_extractor = PlainTextExtractor.new
parser = Nokogiri::HTML::SAX::Parser.new(pt_extractor)
parser.parse_memory(html)
puts pt_extractor.count

Resources