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
Rspec - How to Test Activerecord::Recordnotfound
If Referer = "/Search", Do X, Else Do Y: "Bad Argument (Expected Uri Object or Uri String)"
Does Will Pagination Work with Forms Which Have Method="Post"
Pow Not Loading Gem Properly While Rails S Works
How to Add "Access-Control-Allow-Origin" Headers to API Response in Ruby
PDF Generation Hangs Using PDFkit and Wkhtmotopdf
How to Get Order Username and Provisiondate for All Softlayer MAChines Using Ruby
How to Convert Timestamp with Ruby
Deleting a Line in a Text File
Ruby Selenium Web Drive: How to Find Specific Element by Xpath Div Id and CSS Class
Need Help on Join Table, Limiting Results to Only the Resource Id
Scope That Has Three Levels Deep Joins
Rexml::Document.New How to Give Encode Parameters on This Line
Require Self Made Gem in Jruby Fails After Update to Jruby-1.7.13
Certificate to Pem to Certificate Not Working: Nested Asn1 Error
Rails Migration Complains About Undefined Method 'Attachment' Using Paperclip