Slicing of Arrays in Ruby Returns Different Result - Nil VS. Empty Array

Slicing of arrays in ruby returns different result - nil vs. empty array

It's all in the documentation: http://www.ruby-doc.org/core-2.1.0/Array.html#method-i-5B-5D

Additionally, an empty array is returned when the starting index for an element range is at the end of the array.

Returns nil if the index (or starting index) are out of range.

Why does Ruby Array slicing sometimes returns nil and sometimes returns empty Array?

The first parameter of Array#slice(start,length) is the place between indices where slicing should begin :

array = [1, 2, 3, 4]
# elements : [ 1 2 3 4 ]
# ↑ ↑ ↑ ↑ ↑
# slice start indices : 0 1 2 3 4

slice(0,_) begins left of 1, slice(3,_) begins left of 4, and slice(4,_) begins at the last possible place : right of 4.

slice(4,0) is still inside array, it's the empty Array right of 4.

slice(5,0) isn't inside array anymore, it's nil.

Special cases of Ruby array slicing

Your confusion stems from the lack of a definition for an index being "at the end of the range". For any array arr, the index i is at the end of the range when i equals arr.size, that is, when the index equals the index of the last element plus one.

For a[5,1] #=> [], 5, the starting index, is "at the end of the range", since a.size #=> 5. Therefore, by the doc, this should return an empty array regardless of the value of the second argument (which is better described as the "size", rather than the "index"). For example,

a[5,1]          #=> [] 
a[5, 1_000_000] #=> []
a[6,1] #=> nil

For a[5..10] we have the same result.

Why does array.slice behave differently for (length, n)

Consider this

a = [0, 1, 2, 3] #=> [0, 1, 2, 3]
a[0, 10] #=> [0, 1, 2, 3]
a[1, 10] #=> [1, 2, 3]
a[2, 10] #=> [2, 3]
a[3, 10] #=> [3]
a[4, 10] #=> []
a[5, 10] #=> nil

So a[4, 10] is the slice between the 3 and the end of the array which is []

Where as a[4] and a[5, 10] are accessing elements that aren't in the array

It may help to think of the slice points as being between the elements, rather than the elements themselves.

[ <0> 0 <1> 1 <2> 2 <3> 3 <4> ]

Where <n> are the points between elements and the start/end of the array. a[4, 10] then becomes a selection of 10 elements, starting from point 4. Whereas a[5, 10] starts from point 5, which is not part of the list.

array[4,0] returns [], but array[5,0] returns nil... why?

The [i, n] form is identifying substring boundaries and not characters

The short answer is that you are defining a substring to either return or replace.

There is a zero-length string at the beginning and at the end that needs to be identifiable.

In the two-argument index, the positions are really the individual boundaries between the characters, and there is one such boundary after the last character.

Slicing based on a condition, else return empty array

You can use the array's size as a fallback:

arr[(indices.max || arr.size)..-1]

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

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
=> []


Related Topics



Leave a reply



Submit