`<=': comparison of String with nil failed (ArgumentError) - ruby

I am testing whether array elements are greater than or equal to elements of smaller indices.
I get the error message from the subject line if I use the following loop
return true if order.each_index {|i| order[i ] <= order[i+1]}
I understand the last element of my array(order) can't be compared to a non-existant element.
Comparing a value to nil is impossible.
I don't, however understand why the following loop doesn't return the same error
(0...(order.length - 1)).all? do |i|
order[i] <= order[i + 1]
end
It seems that at some point, i = order.length-1
This means order[i+1] is a nil value (order.length)
Apparently not?

No, because three dots ... here (0...(order.length - 1)) mean 'without last element', so last value would be order.length - 2.
You'll encounter the same error if you try (0..(order.length - 1)).
Check Range documentation:
Ranges may be constructed using the s..e and s...e literals
Those created using ... exclude the end value

Related

Beginner array iteration & error code interpretation

I kept getting the following error. After some research I assumed this is because my array access was throwing the error due to (mistakenly) having a NIL value.
my_solution.rb:24:in `count_between': undefined method `>=' for nil:NilClass
(NoMethodError) from my_solution.rb:35:in `<main>'
I'm new to reading error codes, so perhaps that's where I went wrong. But it got tunnel vision on line 24, as the error suggested. However I couldn't fix it, so out of desperation I wound up randomly changing the (<=) on line 23 to just (<). This fixed it.
Why did this fix it? My only guess is that originally using (<=) made it iterate "too far" and thus somehow returned NIL?
Why did the error code say it was the element on line 24 causing the issue, when it was actually the element on line 23? I'm new and am trying to be less intimated by error codes, so this was a curious experience.
Thanks for any guidance.
# count_between is a method with three arguments:
# 1. An array of integers
# 2. An integer lower bound
# 3. An integer upper bound
#
# It returns the number of integers in the array between the lower and upper
# bounds,
# including (potentially) those bounds.
#
# If +array+ is empty the method should return 0
# Your Solution Below:
def count_between(list_of_integers, lower_bound, upper_bound)
if list_of_integers.empty?
return 0
else
count = 0
index = 0
##line 23##
while index <= list_of_integers.length
##line24##
if list_of_integers[index] >= lower_bound &&
list_of_integers[index] <= upper_bound
count += 1
index += 1
else
index += 1
end
end
return count
end
end
puts count_between([1,2,3], 0, 100)
The last index that's <= list_of_integers.length is outside of the array, since the first index of an array is 0 and the last is array.length - 1.
The reason your error says line 24 is that line 23 works fine --- it just computes that the value of index is less than or equal to the the length of the array. Once you try and reference the element at that index in the array, however, it's assigned nil - and you can't perform a >= operation on nil.
One thing that might be helpful here is firing up an irb. If you try to reference an element that's out of bounds, you'll just get nil. If you try and perform an operation (that's not listed in nil.methods) on that same reference, it'll throw the error you're seeing.

euler project 3 ruby> why does this solution work?

def large_prime(n)
return [] if n==1
factor = (2..n).find {|x| n % x == 0}
[factor] + large_prime(n/factor)
end
I got this solution from somewhere else. I don't understand the 4th line of code where large_prime is called recursively and appended onto factor.
When I change the first line "return []" and leave out the '[]' after the return, I get an error message for on line 4, that says '+':no implicit conversion of nil into Array.
So why does this code work? Thanks
P.S. I'm obviously a noob and everything is very new to me.
The 3rd line finds the first divisor of n between 2 and n. This line itself does not involve recursion.
I don't really get the code you modified, but it seems to return nil in some case, while the original method always return an Array.
You must return an empty array when passed 1 to terminate the recursion. Any positive argument other than one will result in another call to large_prime, but an argument of 1 results in large_prime simply returning an empty array.
At each level of recursion, the program adds an array with the single factor it found to an array consisting of all factors found for the value n/factor. When the last factor (other than 1) is found, the final call to large_prime is made with an argument of 1, large_prime returns an empty array which is then added to the array containing the last factor, giving an array containing just the last factor. This array is then returned and you have
[next-to-last-factor] + [last-factor], giving a return array of [next-to-last-factor, last-factor] which is added to [next-to-next-to-last-factor] giving [next-to-next-to-last-factor, next-to-last-factor, last-factor]. This is then added to an array [next-to-next-to-next-to-last-factor], giving... lather, rinse, repeat until we reach the largest factor and add it in.
You must return an empty array because you can't add nil to an array in Ruby.

Rails iteration math operator causes a wrong number of arguments

This iteration prints the highest index value.
It works and prints 8 eight times.
- #videos.each_with_index do |video, index|
= index.size
When I add a math operator it doesn't work and gives me this error: wrong number of arguments (1 for 0)
- #videos.each_with_index do |video, index|
= index.size - 1
index in your example is a Fixnum, the index of the element in the enumeration. Fixnum#size returns the number of bytes in the machine representation of a Fixnum. Probably not what you were looking for. It accepts zero arguments which explains the exception you got.
The fact that index.size returns 8 is because you're running on a 64bit architecture and has nothing to do with the size of #videos.
It seems like:
index.size - 1
is being interpreted as:
index.size(-1)
Try adding in parenthesis to force it to be interpreted in the correct way:
(index.size) - 1
or:
index.size() - 1

Range and the mysteries that it covers going out of my range

I am trying to understand how range.cover? works and following seems confusing -
("as".."at").cover?("ass") # true and ("as".."at").cover?("ate") # false
This example in isolation is not confusing as it appears to be evaluated dictionary style where ass comes before at followed by ate.
("1".."z").cover?(":") # true
This truth seems to be based on ASCII values rather than dictionary style, because in a dictionary I'd expect all special characters to precede even digits and the confusion starts here. If what I think is true then how does cover? decide which comparison method to employ i.e. use ASCII values or dictionary based approach.
And how does range work with arrays. For example -
([1]..[10]).cover?([9,11,335]) # true
This example I expected to be false. But on the face of it looks like that when dealing with arrays, boundary values as well as cover?'s argument are converted to string and a simple dictionary style comparison yields true. Is that correct interpretation?
What kind of objects is Range equipped to handle? I know it can take numbers (except complex ones), handle strings, able to mystically work with arrays while boolean, nil and hash values among others cause it to raise ArgumentError: bad value for range
Why does ([1]..[10]).cover?([9,11,335]) return true
Let's take a look at the source. In Ruby 1.9.3 we can see a following definition.
static VALUE
range_cover(VALUE range, VALUE val)
{
VALUE beg, end;
beg = RANGE_BEG(range);
end = RANGE_END(range);
if (r_le(beg, val)) {
if (EXCL(range)) {
if (r_lt(val, end))
return Qtrue;
}
else {
if (r_le(val, end))
return Qtrue;
}
}
return Qfalse;
}
If the beginning of the range isn't lesser or equal to the given value cover? returns false. Here lesser or equal to is determined in terms of the r_lt function, which uses the <=> operator for comparison. Let's see how does it behave in case of arrays
[1] <=> [9,11,335] # => -1
So apparently [1] is indeed lesser than [9,11,335]. As a result we go into the body of the first if. Inside we check whether the range excludes its end and do a second comparison, once again using the <=> operator.
[10] <=> [9,11,335] # => 1
Therefore [10] is greater than [9,11,335]. The method returns true.
Why do you see ArgumentError: bad value for range
The function responsible for raising this error is range_failed. It's called only when range_check returns a nil. When does it happen? When the beginning and the end of the range are uncomparable (yes, once again in terms of our dear friend, the <=> operator).
true <=> false # => nil
true and false are uncomparable. The range cannot be created and the ArgumentError is raised.
On a closing note, Range.cover?'s dependence on <=> is in fact an expected and documented behaviour. See RubySpec's specification of cover?.

Using a block to find values matching criteria

To me this makes perfect sense:
triple = dice.collect {|value| if (dice.count(value) >= 3)} ---> Syntax error
OR
triple = dice.collect {|value| dice.count(value) >= 3} ----> Array of true/false
I want the value of the number, not the true or falsity of dice.count(). I know there must be a simple way of doing this.
It sounds like you want Array#select, not Array#collect (also known as Array#map).
collect/map will take each value and put the results of your block into an array. This is why you're seeing an array of true/false.
select will take each value, and return it as a member of an array if the block evaluates to true:
triple = dice.select{ |value| dice.count(value) >= 3 }
Your block needs to return whatever it is you want in the final array.
triple = dice.collect {|value|
if dice.count(value) >= 3
dice.count(value)
end
}
Note that this will return nil for elements < 3 (though you can add an else to return 0 or something). If you only want elements that match your query, you'll need to use dice.select()
As for your first code snippet,
triple = dice.collect {|value| THE_CODE_BLOCK_STARTS_HERE }
Thus, if (dice.count(value) >= 3) is an incomplete if statement. That's why you get syntax error.

Resources