Indexing an Array with a Float
Question
What's the return value of the following Ruby code?
a = [21, 42, 84]
a[1.2] # => ???
The correct answer is
-
It raises
ArgumentError -
[42, 84] -
42Correct -
nil
Explanation
TL;DR
Array#[] does not require an Integer. A Float index is converted to an integer by truncating the fractional part, so a[1.2] reads index 1 and returns 42. No rounding takes place and no error is raised.
Step by step
a = [21, 42, 84]
a[1.2] # => 42
a[1.9] # => 42
1.9 also reads index 1: the conversion truncates, it never rounds. For negative floats the truncation goes toward zero, which matters because it differs from floor:
a = [21, 42, 84]
a[-1.5] # => 84
-1.5.to_int # => -1
-1.5.floor # => -2
-1.5 becomes index -1, the last element, not -2.
Under the hood
Array indexing converts its argument to a C integer. For a Float that is a direct truncation; for any other object, Ruby calls its to_int method (implicit integer conversion). Any object implementing to_int works as an index:
class MyIndex
def to_int = 2
end
a = [21, 42, 84]
a[MyIndex.new] # => 84
An object without to_int fails. Strings deliberately do not implement it, so there is no accidental "1" indexing:
a = [21, 42, 84]
begin
a["1"]
rescue TypeError => e
e.message # => "no implicit conversion of String into Integer"
end
The same conversion applies to fetch, dig, at, and Array#[]=:
a = [21, 42, 84]
a.fetch(1.2) # => 42
a.dig(1.2) # => 42
Edge cases
The float must fit in the integer range after truncation, and it must be a real number. Otherwise the conversion raises RangeError:
a = [21, 42, 84]
begin
a[1.2e100]
rescue RangeError => e
e.message # => "float 1.2e+100 out of range of integer"
end
begin
a[Float::NAN]
rescue RangeError => e
e.message # => "float NaN out of range of integer"
end
Truncating float indices is convenient in quick scripts, but an index that arrives as 1.2 usually signals a bug upstream. Convert explicitly (idx.to_i, or Integer(idx) to reject non-integral input) when the intent matters.
Share this quiz
Comments
No comments yet. Be the first.