Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby: Range is empty, but slicing with it produces elements

I'm learning Ruby and have just gotten into some stuff about arrays and ranges. I've run into something about slices that, while it makes sense at first glance, confuses me a bit when I look deeper into it.

IRB says that (2..-1).to_a is an empty array, meaning no values in the range, right?
But if I use that same range in [:a, :b, :c, :d, :e][2..-1], I get back [:c, :d, :e] rather than an empty array.

Now, I'm aware that -1 represents the last element of the array, so it kind of makes sense that what got picked, did. But if the range itself would be empty, how is it selecting anything?

like image 910
cHao Avatar asked Oct 01 '10 08:10

cHao


2 Answers

This is a fascinating question. The answer is that it's not the individual elements of the range that are inspected when slicing the array, but the first and last elements. Specifically:

>> (2..-1).to_a
=> []
>> (2..-1).first
=> 2
>> (2..-1).last
=> -1

Thus the example works, since it slices the array from the [2] element to the [-1] element.

If you want a consistent way to think about this, consider that (2..-1).to_a outputs the integers found between 2 and -1 (of which there are none), but that [2..-1] means from the 2 index to the -1 index.

(Source: array.c and range.c in the Ruby source.)

And, the complicated bonus part: to get the meaning you were thinking about, you could use

>> [:a, :b, :c, :d, :e].values_at *(2..-1).to_a
=> []
like image 142
Peter Avatar answered Sep 23 '22 15:09

Peter


In the slicing operation it's not seeing the range as a Range per se, it's just looking at the values of the endpoints, in this case 2 and -1. Since -1 has a special meaning (i.e last item in the list) it just returns everything from item at index 2 to the end of the list. Don't think of it as a Range here, just think of it as a convenient way of passing in two numbers (expressing two end points).

like image 43
horseyguy Avatar answered Sep 20 '22 15:09

horseyguy