JaTo
JaTo

Reputation: 2832

Ruby Logic comparisons with Nil

I am having trouble understanding the following code:

vowels_arr = ["a","e","i","o","u"]
(0...(vowels_arr.length - 1)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]}

When I try to run it WITHOUT the - 1, I get an error saying that I can't compare a string to nil. But what I dont understand is that why do we even need the -1?? The "..." ranger makes it so we are only evaluating "a","e","i","o" (4 out of the 5). Since the total length is 5 and we are already at 4 things to compare, my belief is that the comparison (vowels_arr[i] <= vowels_arr [i+1]) should work without the -1.

Can someone please explain to me why we need the -1 after array length?

Also are there other ways in ruby to get past this comparing to nil error?

Upvotes: 0

Views: 356

Answers (3)

sawa
sawa

Reputation: 168081

As Sergio answers, the problem is with vowels_arr[i + 1]. The variable i ranges over the indices of vowels_arr, and hence i + 1 will not necessarily point to an existing index of vowels_arr. Particularly, when i reaches the last index, i + 1 will be greater than the existing indices, and vowels_arr[i + 1] will be nil.

Also as Sergio answers, if your purpose is to see if it is sorted, then doing as Sergio's answer is straightforward, but in general cases, you can do it like this:

vowels_arr.each_cons(2).all?{|e1, e2| e1 <= e2}

Upvotes: 1

Sergio Tulentsev
Sergio Tulentsev

Reputation: 230286

It's because of this:

vowels_arr[i + 1]

(0...(vowels_arr.length)) will return all indexes for the array.

(0...(vowels_arr.length)).to_a # => [0, 1, 2, 3, 4]

But then you're trying to get next index from current. If current index is last index (4), this results in an error, because you get nil where you expect a string (because element doesn't exist at non-existent index). That's why you need length - 1, to allow your logic not to go out of array's bounds.

By the way, if you're trying to check if the array is sorted, why not do it more directly?

vowels_arr = ["a","e","i","o","u"]
puts vowels_arr.sort == vowels_arr 
# >> true

Upvotes: 2

Arup Rakshit
Arup Rakshit

Reputation: 118261

vowels_arr = ["a","e","i","o","u"]
p vowels_arr[vowels_arr.length] #=> nil
(0..(vowels_arr.length)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]}
#=> `<=': comparison of String with nil failed (ArgumentError)

As you are passing the vowels_arr[vowels_arr.length] element to the block,which is nil. In Ruby array's are 0(zero) based. Thus vowels_arr.length gives 5 means elements are in the range of (0..4). see below:

vowels_arr = ["a","e","i","o","u"]
p vowels_arr[0] #=> "a"
p vowels_arr[1] #=> "e"
p vowels_arr[2] #=> "i"
p vowels_arr[3] #=> "o"
p vowels_arr[4] #=> "u"
p vowels_arr[5] #=> nil
p vowels_arr[6] #=> nil

(0..(vowels_arr.length)) means you are passing to the block 0,1,2,3,4,5, and an attempt to access 5 gives nil, as in your array in 5th index is nil. See why the code (0...(vowels_arr.length)).all? {|i| vowels_arr[i] <= vowels_arr[i + 1]} failed by the below debugging with each to see what has been passed to the block:

vowels_arr = ["a","e","i","o","u"]
(0...(vowels_arr.length)).each {|i| p vowels_arr[i],"--",vowels_arr[i+1]}
p (1...3).to_a

Output:

"a"
"--"
"e"
"e"
"--"
"i"
"i"
"--"
"o"
"o"
"--"
"u"
"u"
"--"
nil

Upvotes: -1

Related Questions