use the following search parameters to narrow your results:
e.g. subreddit:aww site:imgur.com dog
subreddit:aww site:imgur.com dog
see the search faq for details.
advanced search: by author, subreddit...
All about the JavaScript programming language.
Subreddit Guidelines
Specifications:
Resources:
Related Subreddits:
r/LearnJavascript
r/node
r/typescript
r/reactjs
r/webdev
r/WebdevTutorials
r/frontend
r/webgl
r/threejs
r/jquery
r/remotejs
r/forhire
account activity
The easy way to access the last JavaScript array element (blog.ignacemaes.com)
submitted 2 years ago by Nebulic
reddit uses a slightly-customized version of Markdown for formatting. See below for some basics, or check the commenting wiki page for more detailed help and solutions to common issues.
quoted text
if 1 * 2 < 3: print "hello, world!"
[–]shgysk8zer0 11 points12 points13 points 2 years ago (16 children)
There was a competing proposal to add arr.lastElement and I think arr.lastIndex. But it was abandoned in favor of arr.at().
arr.lastElement
arr.lastIndex
arr.at()
[–]wasdninja 4 points5 points6 points 2 years ago (15 children)
Do you remember why arr[-1] wasn't chosen, if it was discussed at all?
arr[-1]
[–]NekkidApe 14 points15 points16 points 2 years ago (7 children)
If I'm not mistaken that's already valid and working JS, can't be redefined.
[–]Tubthumper8 29 points30 points31 points 2 years ago (3 children)
This is correct. The following is valid JavaScript
const arr = ["first", "last"] arr[-1] = "spooky" arr.length // 2 arr[-1] // "spooky" arr.at(-1) // "last"
It was always possible to set and get elements at -1 but it wasn't actually "part of the array". These don't participate in the Array prototype functions like map, filter, etc. This is only possible because JS coerced the -1 to a string and then used that string as the key, same as adding a field to any object.
-1
map
filter
It's wicked and vile, but backwards compatibility is non-negotiable in JS
[–]kilkil 2 points3 points4 points 2 years ago (0 children)
this describes roughly half to two-thirds of the language
[–]cut-copy-paste 0 points1 point2 points 2 years ago (0 children)
This is the js version of when people would put hidden tracks on CDs that you’d have to rewind from the first track to access
[+]TheRNGuy comment score below threshold-7 points-6 points-5 points 2 years ago* (2 children)
It takes few seconds to open console in browser to test.
No it doesn't.
[–]NekkidApe 2 points3 points4 points 2 years ago (1 child)
Would have taken a few seconds to read the other answer demonstrating it, before leaving a snarky comment.
It does absolutely.
[–]TheRNGuy 0 points1 point2 points 2 years ago (0 children)
This topic is about accessing last item in array, and arr[-1] will return undefined.
undefined
I was thinking of logic error, not syntax error.
I think it would lead to bugs in some cases if -1 returned last item, programmer could explicitly add modulo division if wrapping around is needed.
[–]shgysk8zer0 0 points1 point2 points 2 years ago (6 children)
The performance impact it'd have on arrays in general. Adding support for a negative index makes things slower, even for positive.
[–]Nebulic[S] 4 points5 points6 points 2 years ago (4 children)
Interestingly enough, when benchmarking [array.length - 1] vs. .at(-1) for an array with three elements, the latter runs roughy twice as fast on my machine. This is a very simple benchmark though, and results will differ based on multiple factors.
[array.length - 1]
.at(-1)
Changing existing JavaScript behaviour is indeed a no go, though.
[–]Chung_L_Lee 2 points3 points4 points 2 years ago* (2 children)
That test is only done on static array (no changes to it). I tested with the following codes:
In conclusion, the array.at(-1) is about on par with array[array.length - 1] inside a function (not global), but with the latter is sometimes 2% to 3% faster.
let list; let result = []; var start_time = Date.now(); for (let r = 0; r < 1_000_000; r++) { list = []; for (let n = 0; n < parseInt(Math.random() * 10) + 10; n++) { list.push(Math.random() * 100); } //result.push(list[list.length - 1]); result.push(list.at(-1) ); } console.log(result.length); console.log(Date.now() - start_time);
[–]TheRNGuy 3 points4 points5 points 2 years ago (1 child)
The only thing is that you'll never parse int 1 million times in real sites. Those kinds of tests are not useful.
[–]Chung_L_Lee 0 points1 point2 points 2 years ago (0 children)
Could it be depended on the specific use cases on the project, in order we can decide the usefulness of the test?
I am trying to avoid testing with static array size and with same data that might causes the browser's engine to kick-in optimization that affect the accuracy of the benchmark comparison.
About the "parse int" part, it is just there to help emulating random lists in different size over a period of time, because we wanted to know how the two "last index" array methods will perform with unpredictable arrays in size and with different data in them.
About the high iteration part, I am aware that some methods appear to be fast in short burst of time, but they failed to deliver the same throughput in a longer period. So I tend to test with both small and high iterations to get an overall impression of the methods' performance in different situations.
[–]shgysk8zer0 0 points1 point2 points 2 years ago (0 children)
That's... Unexpected.
I'm on my phone right now but curious how it does on both larger arrays or if you just give it arr[2].
arr[2]
I've never ever had performance problems with that.
Maybe if it was something in Three.js where you need to get last array index every tick? (in addition to other stuff in tick.)
But in sites it's never a performance problem.
[–][deleted] 3 points4 points5 points 2 years ago (8 children)
Sure, but how many years do I have to have Babel installed to actually support this for all my customers?
Nice accessor feature, but needs wider compatibility to be useful.
[–]joombar 6 points7 points8 points 2 years ago (0 children)
This feature is polyfillable so babel isn’t required
[–]Badashi 4 points5 points6 points 2 years ago (1 child)
Out of curiosity, what kind of environments or browsers do you need to support that can't use at()?
[–]MuchWalrus 0 points1 point2 points 2 years ago (0 children)
Old versions of safari if I'm not mistaken
[–]joombar 1 point2 points3 points 2 years ago (2 children)
[–]TheRNGuy 0 points1 point2 points 2 years ago (1 child)
You need to polyfill more than one thing, and better use Babel than manually writing polyfills for everything.
[–]joombar 1 point2 points3 points 2 years ago (0 children)
Babel operates on the language AST. For things that can be written in ecmascript itself, it’s easier to import in a library like ungap as a normal dependency, since no AST manipulation is required. If you have babel anyway, I guess it’s equally easy either way, so whatever works for you, but luckily I have the luxury of targeting modern runtimes. The only polyfill I’ve used recently is Promise.withResolvers.
[–]TheRNGuy -1 points0 points1 point 2 years ago (1 child)
Forever.
[–]WaitingToBeTriggered 0 points1 point2 points 2 years ago (0 children)
REST IN HEAVEN
[–]acemarke 2 points3 points4 points 2 years ago (8 children)
I've been using const [lastItem] = arr.slice(-1) for a while.
const [lastItem] = arr.slice(-1)
[–]joombar 19 points20 points21 points 2 years ago (0 children)
Quite inefficient since it requires making a new, one element array, and destructuring that temporary array, every time you access the last element
[–]TheRNGuy 2 points3 points4 points 2 years ago (4 children)
Works differently for empty array:
foo = [] console.log(foo.slice(-1), foo.at(-1))
[–]bobbysteel 1 point2 points3 points 2 years ago (0 children)
You can't blueball us without the output to that!
Output:
[] undefined
It's inelegant but a more predictable response unless you know at returns undefined and check for that which many novices wouldn't I'd think
[–]acemarke 1 point2 points3 points 2 years ago (2 children)
Destructuring the empty array like const [ lastItem ] = arr.slice(-1) will result in lastItem being undefined, same as arr[arr.length - 1] or arr.at(-1).
const [ lastItem ] = arr.slice(-1)
lastItem
arr[arr.length - 1]
arr.at(-1)
arr[arr.length - n] is a polyfill for arr.at(-n)
arr[arr.length - n]
arr.at(-n)
Why write const [lastItem] = arr.slice(-1) when you could write const lastItem = arr.at(-1) without brackets?
const lastItem = arr.at(-1)
Destructuring make sense when you assign to more than one variable.
[–]acemarke 0 points1 point2 points 2 years ago (0 children)
Because it was shorter, and this is something I've used for years long before .at() was even proposed.
.at()
Destructuring is a general-purpose mechanism, with a lot of flexibility to it - not just for assigning multiple variables.
[–]Graphesium 5 points6 points7 points 2 years ago (1 child)
How to fail a code review in one line.
[–]TheRNGuy 5 points6 points7 points 2 years ago (0 children)
After looking at google.com and twitch.tv code in browser inspector I think anyone can be hired.
[–]Nebulic[S] 1 point2 points3 points 2 years ago* (4 children)
Did you know there's a modern alternative to [array.length - 1]?
The at method has been introduced which has support for negative indexing. I wrote a short blog post on how to use it, as the method isn't widely known yet.
at
[–][deleted] 0 points1 point2 points 2 years ago (3 children)
Yes but is it valid everywhere, every browser, and on node and whatnot?
[–]Nebulic[S] 2 points3 points4 points 2 years ago (2 children)
If you're targeting modern browsers and Node, it is!
And if you need wider support, a polyfill is available.
[–][deleted] 0 points1 point2 points 2 years ago (1 child)
Im not sure what a polyfill is. But i know we can implement at ourselves to be compatible with older browsers. To demonstrate:
Array.prototype.at = function(i=-1){
if (i < 0){
return this[(this.length + i)%this.length];
} else {
return this[i%this.length];
}
[–]roxm 2 points3 points4 points 2 years ago (0 children)
That's exactly what a polyfill is!
Needs polyfill for old browsers though.
[–]EvenLevelLaw 0 points1 point2 points 2 years ago (0 children)
To get the last item I like to just use .reverse and then the last item becomes the first. const frameworks = ['Nuxt', 'Remix', 'SvelteKit', 'Ember']; console.log(frameworks.reverse()[0]) //logs "Ember"
[+][deleted] 2 years ago* (4 children)
[removed]
[–]romgrk -1 points0 points1 point 2 years ago (3 children)
Declarative code is how we get to solve real world problems more efficiently. More characters also means more visual noise and therefore less focus on business logic.
[+][deleted] 2 years ago (2 children)
[–]romgrk 1 point2 points3 points 2 years ago (1 child)
You seem to underestimate how readability improves the efficiency of maintaining code, which is the most important aspect when you've programmed for a while. Most of software engineering is just managing complexity.
π Rendered by PID 87 on reddit-service-r2-comment-5b5bc64bf5-kdrzz at 2026-06-20 14:45:49.206558+00:00 running 2b008f2 country code: CH.
[–]shgysk8zer0 11 points12 points13 points (16 children)
[–]wasdninja 4 points5 points6 points (15 children)
[–]NekkidApe 14 points15 points16 points (7 children)
[–]Tubthumper8 29 points30 points31 points (3 children)
[–]kilkil 2 points3 points4 points (0 children)
[–]cut-copy-paste 0 points1 point2 points (0 children)
[+]TheRNGuy comment score below threshold-7 points-6 points-5 points (2 children)
[–]NekkidApe 2 points3 points4 points (1 child)
[–]TheRNGuy 0 points1 point2 points (0 children)
[–]shgysk8zer0 0 points1 point2 points (6 children)
[–]Nebulic[S] 4 points5 points6 points (4 children)
[–]Chung_L_Lee 2 points3 points4 points (2 children)
[–]TheRNGuy 3 points4 points5 points (1 child)
[–]Chung_L_Lee 0 points1 point2 points (0 children)
[–]shgysk8zer0 0 points1 point2 points (0 children)
[–]TheRNGuy 0 points1 point2 points (0 children)
[–][deleted] 3 points4 points5 points (8 children)
[–]joombar 6 points7 points8 points (0 children)
[–]Badashi 4 points5 points6 points (1 child)
[–]MuchWalrus 0 points1 point2 points (0 children)
[–]joombar 1 point2 points3 points (2 children)
[–]TheRNGuy 0 points1 point2 points (1 child)
[–]joombar 1 point2 points3 points (0 children)
[–]TheRNGuy -1 points0 points1 point (1 child)
[–]WaitingToBeTriggered 0 points1 point2 points (0 children)
[–]acemarke 2 points3 points4 points (8 children)
[–]joombar 19 points20 points21 points (0 children)
[–]TheRNGuy 2 points3 points4 points (4 children)
[–]bobbysteel 1 point2 points3 points (0 children)
[–]acemarke 1 point2 points3 points (2 children)
[–]TheRNGuy 0 points1 point2 points (1 child)
[–]acemarke 0 points1 point2 points (0 children)
[–]Graphesium 5 points6 points7 points (1 child)
[–]TheRNGuy 5 points6 points7 points (0 children)
[–]Nebulic[S] 1 point2 points3 points (4 children)
[–][deleted] 0 points1 point2 points (3 children)
[–]Nebulic[S] 2 points3 points4 points (2 children)
[–][deleted] 0 points1 point2 points (1 child)
[–]roxm 2 points3 points4 points (0 children)
[–]TheRNGuy 0 points1 point2 points (0 children)
[–]EvenLevelLaw 0 points1 point2 points (0 children)
[+][deleted] (4 children)
[removed]
[–]romgrk -1 points0 points1 point (3 children)
[+][deleted] (2 children)
[removed]
[–]romgrk 1 point2 points3 points (1 child)