end()/atEnd() to allow only negative index for more efficiency than at()

GitHub - tc39/proposal-relative-indexing-method: A TC39 proposal to add an .at() method to all the basic indexable classes (Array, String, TypedArray) <-- Rather than allowing both positive and negative index as in at() proposal here, we should also have end() or atEnd() which allow only negative index, which is more simpler and more efficient than at()

function end(i) { return this[this.length+i] } <-- It should be as much simple like this

Proposal: GitHub - tc39/proposal-relative-indexing-method: A TC39 proposal to add an .at() method to all the basic indexable classes (Array, String, TypedArray)

You're a little bit late :( - I doubt both .at() and .end() would get into the language, as they seem to be solving the same problem in almost the same way, just with slightly different behaviors. If .at() was still at stage 2, I would suggest opening an issue there to make this suggestion, but as it's at stage 3 now, it won't be open for these kinds of major changes.

I'm surprised this sort of idea was never discussed on that proposal (at least, not from what I could see in the issues list).

How exactly is that more efficient? If you want to allow only negative indexes, that means you have to check the sign, in which case you're doing the exact same amount of work.

at(i) {
  if (i < 0) return this[this.length + i];
  else return this[i];
}
// versus
atEnd(i) {
  if (i < 0) return this[this.length + i];
  else throw new Error("invalid index");
}
1 Like

I mean to always return this[this.length+i], for both positive and negative index, leaving it return undefined for positive index, so no need to check sign.

It is very very rare that at() will be used for positive index, so why need to have it handle positive index?

It's rare that you would need to micro-optimize to that degree in a given program, and if that level of micro-optimization is needed (e.g. in a deeply nested loop), you're should already be doing all sorts of readability sacrifices to improve speed. Manually doing array[array.length-1] shouldn't be a big concern in this scenario, plus it takes out a function call, which may further optimizes your code.

If the main reason you want this feature is for performance, I can't get behind that, for the reason listed above. If the main reason you prefer this feature is you find it more understandable/readable, then I can at least appreciate that point of view (even if I don't share that opinion)

1 Like

Because that is pretty useful when you get the index as argument from outside. Instead of branching on the sign in your code, you let the .at() method do that, because for a built-in that test is practically free, after all the other stuff it has to do before it even gets to that point.

1 Like

array[array.length-1] is not that easy when deal with array returned by a function, but with end(), it will be simple like array.filter(...).end(-1), rather than manually doing var a=array.filter(...); a[a.length-1].
My main reason is for both performance and readibility.
end() as a native function will allow it to have an implementation of end() that is more efficient than doing array[array.length+i] manually.

array[array.length-1] is not that easy when deal with array returned by a function, but with end(), it will be simple like array.filter(...).end(-1)

But it's not that hard. The point I'm trying to make is if you actually care about the extremely minor performance impact of this in a certain area of your code, then you're probably doing all sorts of unreadable things. For example, you would use a c-style loop for (let i = 0; i < length; ++i) instead of a for-of loop because those are faster, and you would use ~~ instead of Math.floor(), and to check if something is an array, you would do yourArray.constructor === Array instead of Array.isArray(yourArray), and you wouldn't use .filter(), .map(), or .reduce() like you just mentioned, as for loops are much faster than those, and you would inline as many functions as possible (function calls aren't free), and you would write extremly-difficult-to-read-and-understand branchless code, if you're using any libraries in that section of code, then maybe you'll handwrite the portions you need so you can squeeze out some extra performance, etc.

But that's not all - the biggest performance hits often come from locallity issues, not from these tiny micro-optimizations, so you might take a look at restructuring how you're doing things in uglier way that improve locallity (see here).

At this point, it might be worth just writting this section of code in a different language and compiling it to WebAssembly instead, as you'll get much better performance out of that.

(note that I keep talking about a section of code. No one should micro-optimize an entire application, only the parts that need it most. If you micro-optimizae an on-click handler, would the end user ever notice the difference? Nope. It doesn't need it, so don't make code unreadable and bug prone to give something they don't need).

The point is, it's unreasonable to complain about the performance of array.at() unless you're actually writing code that's doing all sorts of unreadable micro-optimizations as described above, many of which create a much bigger performance improvement. If you're not, then you don't have a use case for an ever-so-slightly faster version of array.at(). If you are, then using array[array.length - 1] shouldn't add much relative unreadability compared to the rest of the gross stuff being done (and it's possible a browser could optimize array[array.length - 1] to be just as fast as array.end(-1) - I don't know for sure`)

2 Likes

This is so incredibly inefficient that it's funny. What you're looking for is array.findRight(...) which I think someone here suggested in another topic ;)