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?
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
=> []
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).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With