I think there is room for—and a solid case to be made for—an alternative to the Array.prototype.at
method that would take a finite integer as its sole argument, the index value of the desired element.
Similar to Array.prototype.at
, Array.prototype.idx
would support negative index values, but contrastingly, it would not be syntactical sugar for arr.slice(n)[0]
but rather implement a new spec that would only accept an integer as its sole argument, representing the desired index.
In the case of negative values, negative indices would start at -0
(not -1
) and descend from there.
Using the array ["a", "b", "c", "d", "e"]
for example…
const arr = ["a", "b", "c", "d", "e"]
arr.idx(0) // -> "a"
arr.idx(1) // -> "b"
arr.idx(-0) // -> "e"
arr.idx(-1) // -> "d"
Adding a method supporting a -0
index would be vital in equal polarity in comparison. If a function takes in an index offset value as one argument and a direction (e.g. LFT/asc, RTL/dsc), the return value would be a simple calculation of the offset multiplied by the direction where the direction would be represented as either 1
or -1
which may in practice be calculated using Math.sign
.
Using Array.prototype.at
, this would require several additional checks…
- Firstly, checking to ensure the passed value is indeed a valid finite integer
- Secondly, checking to see if the direction is RTL/dsc and if the computed value would yield a negative index value, the function would have to subtract one from the desired negative offset value.
For example, if someone desired a value with an offset of 1
from the end of the array (the second from the end), the function would have to return an offset of -1 * (1+1)
which computes to -2
in order to balance the -1
offset Array.prototype.slice
imposes (by nature of length - 1
) and return the desired value.
Using this proposed Array.prototype.idx
method however, the desired return value would always be computed as offset * direction
regardless of whether the value is positive or negative.
An offset of 0
from the end of the array (RTL) would be the equivalent of [1, 2, 3].idx(-0)
, yielding 3
.
Regarding the proposed spec, accounting for -0
values is historically possible with logic like this:
function isNeg0(n) {
return n === 0 && Math.sign(Infinity / n) === -1
}
isNeg0(-1) // -> false
isNeg0(-0) // -> true
isNeg0(0) // -> false
isNeg0(1) // -> false
With the newer addition and now widespread support of Object.is()
, detection of negative zero values is as simple as this logic:
function isNeg0(n) {
return Object.is(n, -0)
}
isNeg0(-1) // -> false
isNeg0(-0) // -> true
isNeg0(0) // -> false
isNeg0(1) // -> false
A modern JS spec for a method like Array.prototype.idx
could look as simple as a wrapper of the Array.prototype.at
method, which would not deviate away from the traditional coercion method at all but rather make use of it:
Array.prototype.idx = function(index) {
return this.at(index < 0 || Object.is(index, -0) ? index - 1 : index)
}
const arr = [1, 2, 3, 4, 5]
// positive index values
arr.idx(0) // -> 1
arr.idx(1) // -> 2
arr.idx(2) // -> 3
arr.idx(3) // -> 4
arr.idx(4) // -> 5
// negative index values
arr.idx(-0) // -> 5
arr.idx(-1) // -> 4
arr.idx(-2) // -> 3
arr.idx(-3) // -> 2
arr.idx(-4) // -> 1
// other values
arr.idx("foo") // -> 1
arr.idx(NaN) // -> 1
arr.idx(1.5) // -> 2