DEV Community

Cover image for `at` coming soon to ECMAScript
Laurie
Laurie

Posted on • Originally published at laurieontech.com

`at` coming soon to ECMAScript

If you're a JavaScript developer you've likely used arrays quite a bit. They're an essential data structure within the language.

In fact, they're so essential that the array prototype has seen rapid expansion in the past few years, with things like flat and filter added. And we're not done yet.

Accessor

In order to access an element in an array, you need to know its index. Indeces in JavaScript are zero-based, so the first element is at index 0.

const arr = ["a","b","c","d"]
arr[0] // this is "a"
arr[2] // this is "c"
Enter fullscreen mode Exit fullscreen mode

As you can see in the example above, you can access the first element, or the third element. What about the last element? In other languages you might be able to do something like this.

const arr = ["a","b","c","d"]
arr[-1] // This is NOT "d"
Enter fullscreen mode Exit fullscreen mode

But not in JavaScript! Why not? Well, as it turns out, -1 is already a valid key. Arrays are really objects with indeces as keys. So arr[-1] is looking at the arr object and the value of the "-1" key, which is undefined.

The last element

Then how do we access the last element without knowing its index? There are ways to do it, but its certainly more verbose. You can use the length lookup.

arr[arr.length - 1] // this is "d"
Enter fullscreen mode Exit fullscreen mode

Or you have the slice option.

arr.slice(-1)[0] // this is "d"
Enter fullscreen mode Exit fullscreen mode

Introducing at

That's why the at function is under consideration. Instead of those options, you'd be able to write this instead.

arr.at(-1) // this is "d"
Enter fullscreen mode Exit fullscreen mode

Note that this works for all valid negative indices, up until you pass the first element.

The great thing about at is that it can replace square brackets all together.

arr.at(0) // this is still "a"
Enter fullscreen mode Exit fullscreen mode

And what about an invalid index?

arr.at(5) // this is undefined
Enter fullscreen mode Exit fullscreen mode

Seems pretty all encompassing.

An aside on history

As it turns out, this was attempted before using item. However, it wasn't web compatible as it clashed with major libraries. So, at is the current proposal.

Would you use it?

Hopefully this will move forward to Stage 4 soon and be officially adopted. I can see this being nice syntactic sugar for accessing array elements.

Top comments (77)

The discussion has been locked. New comments can't be added.
Collapse
 
royalfig profile image
Ryan Feigenbaum

Would I use it?
const response = ['No way', 'I don't know', 'Awww yeah'].at(-1)

Collapse
 
hasnaindev profile image
Muhammad Hasnain • Edited

Everybody gangsta until .at(-1) returns undefined.

Collapse
 
jankapunkt profile image
Jan Küster

Would [].at(-1) be like dividing by zero?

 
raulcg profile image
RaulC-G

[].at(-1) == [][[].length - 1] == [][-1] == undefined

Collapse
 
yoursunny profile image
Junxiao Shi

What if you need second-to-last item?
.at(-2) is as reasonable as .at(-1).

Collapse
 
laurieontech profile image
Laurie

Yup, that works fine

Collapse
 
nickytonline profile image
Nick Taylor • Edited

I've used var last = arr[arr.length - 1] in the past, but now with destructuring, I typically do const [, last] = someArray.

I'm not sure I'd use at aside from getting the last item in an array since currently I can do
e.g. someArray[1] which is less typing than someArray.at(1) for elements that are not the last item. I probably would have opted for an Array.prototype.last.

Maybe there are use cases for it that I'm missing like composing a bunch of functions.

Not everyone is seeing my addendum to this comment so here it is

Just an update to my initial comment as I typed it out pretty quickly yesterday. const [, last] = someArray will work if the array was only two items. For example, if it's 4 items, this won't work. You'll end up with this.

const a = [1,2,3,4];
const [,last] = a;

console.log(last); // a === 2
Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode

Enter fullscreen mode Exit fullscreen mode

If I wanted to get the last element in the above array, I'd have to do this.

const a = [1,2,3,4];
const [, , ,last] = a;

console.log(last); // a === 2
Enter fullscreen mode Exit fullscreen mode
Enter fullscreen mode Exit fullscreen mode

Enter fullscreen mode Exit fullscreen mode
</div>
Enter fullscreen mode Exit fullscreen mode

Collapse
 
milichev profile image
Vadym Milichev • Edited

This const [, last] = someArray is equal to const last = someArray[1]
To use destructuring, one might want something like
const [l, [l - 1]: last] = someArray, but it's hardly more readable 😁

Collapse
 
sybers profile image
Stanyslas Bres

@milichev I get the idea from your snippet but I think the syntax is incorrect, it should look something like this :

const { length, [length - 1]: last } = [1, 2, 3, 4];
// last === 4
Enter fullscreen mode Exit fullscreen mode

Anyways that's a funny thing I never thought of :)

Collapse
 
nickytonline profile image
Nick Taylor • Edited

Just an update to my initial comment as I typed it out pretty quickly yesterday. const [, last] = someArray will work if the array was only two items. For example, if it's 4 items, this won't work. You'll end up with this.

const a = [1,2,3,4];
const [,last] = a;

console.log(last); // a === 2
Enter fullscreen mode Exit fullscreen mode

If I wanted to get the last element in the above array, I'd have to do this.

const a = [1,2,3,4];
const [, , ,last] = a;

console.log(last); // a === 2
Enter fullscreen mode Exit fullscreen mode
Collapse
 
davwheat profile image
David Wheatley

Oooh I knew this was possible, but never really put 2 and 2 together with that destructuring for the last item! Thanks for pointing that out to me.

Collapse
 
rpcabrera profile image
Rigoberto • Edited

Even I propose to extend your idea and include all quick methods related to collections like LINQ (from C#) does.

Collapse
 
edo78 profile image
Federico "Edo" Granata

As described here it looks like .at() is barely useful to get the last item.
It's unclear if it can be used to loop through the array in reverse (eg. .at(-2)) because we could have an hard time figuring when we reach the first element and stop the loop.

Collapse
 
hisuwh profile image
Henry Ing-Simmons • Edited
for (let i = 1; i <= arr.length; i++) {
   console.log(arr.at(-i));
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
edo78 profile image
Federico "Edo" Granata

So we still have to use .length like in the classic way

for (let i = arr.length - 1 ; i >= 0; i--) {
  console.log(arr[i]);
}
Enter fullscreen mode Exit fullscreen mode

We just shifted the 1 from the length - 1 to the starting point
I'm struggling to see why we need .at()

BTW You should cange const to let in your example

 
hisuwh profile image
Henry Ing-Simmons

You're right. I never use classic for loops anymore really. Too used to writing:

for (const item of arr) {
    // ...
Enter fullscreen mode Exit fullscreen mode

I agree though. Don't see the point of at. Equally if you wanted to iterate the loop in reverse:

for (const item of arr.slice().reverse()) {
   // ...
Enter fullscreen mode Exit fullscreen mode
 
laurieontech profile image
Laurie

They gave you an example. You really don’t need to “correct” it.

 
edo78 profile image
Federico "Edo" Granata

What's wrong with that? I saw that he already edited it because he used "0" as a starting point instead of "1", so I thought he would like a fix.

 
laurieontech profile image
Laurie

Because they're not wrong. Let is just as valid as const from the perspective of the code. Without larger context it's just a preference. And you understood the example they were kind enough to provide just fine. Your response was to make yourself feel smart.

 
edo78 profile image
Federico "Edo" Granata

Maybe I'm missing somethig as english isn't my first language but using const is wrong.
Javascript prevent you from reassigning a value to a const throwing an error TypeError: Assignment to constant variable.

I was the first one asking a question because I know that I don't know everything and I want to learn so I think others can appreciate if I reciprocate

Collapse
 
laurieontech profile image
Laurie

Yes, you could loop through in reverse. The first non-element would return undefined.

Collapse
 
edo78 profile image
Federico "Edo" Granata

Sadly you can't just check for an undefined element because array with "hole" like const arr = [1,,3,4]; can create a problem with that logic.

Collapse
 
laurieontech profile image
Laurie

Yes, if you have holes then you'll need to check relative to the length of your array.

 
edo78 profile image
Federico "Edo" Granata

sure or you should do as suggested here dev.to/hisuwh/comment/1fcp7
I still miss the value of .at() beside being a sugar syntax to get the last item

 
andrewbridge profile image
Andrew Bridge

It seems like you're pretty passionate about having a last() but I don't really see the issue. at(-1) uses the same number of characters as last() so it's no more verbose, plus it has the added benefit of providing a uniform way of accessing array items from the end of the array regardless of how many positions back from the end you need it.

A multi-purpose solution seems preferable to a method that can only do one thing, no?

That aside, if people start using .at(positiveIndex), that's on them. We already have people swearing off for loops and thinking reduce is the catch all solution for everything, and I think those issues are far more detrimental to this.

Collapse
 
dewaldels profile image
Dewald Els

What ever happend to prototypes?

Array.prototype.last = function() {
  if (this.length > 0) {
    return this[this.length-1];    
  }
  return undefined
}

const items = ['What', 'ever', 'happened', 'to', 'prototypes'];
const lastItem = items.last();
console.log(lastItem);
Enter fullscreen mode Exit fullscreen mode
Collapse
 
laurieontech profile image
Laurie

You can read more about that here: github.com/tc39/proposal-array-last

The TLDR is that at was progressing faster and solved the same problem. So last was no longer pursued.

Collapse
 
mlippert profile image
Mike Lippert

I see responses saying why not arr.last.
I think they are completely missing the point.

True the article used -1 as the example value to get the last element, but the point of .at() is that negative indices start at the end, so -2 is the next to last element and -3 the one before that one, etc.
I'd assume that an index of -n of an n element array would be the first element and any index less than -n is undefined.

In addition .at() lets you use the index value stored in a variable, whereas .last does not.

Collapse
 
dsm637 profile image
Denis Mitropolsky

Honestly, I don't think getting the last is the real reason for "at"

Collapse
 
fjones profile image
FJones

Well then... What is? The only benefits over indices seem to be that it throws on non-array and that you can use valid negative indices.

For negative indices, the main purpose is most definitely .at(-1), since anything dynamic still requires knowing the .length, and anything less than -1 implies a data structure that should probably be an object in the first place.
And if I want to throw on non-array, I'll just use TypeScript.

 
dsm637 profile image
Denis Mitropolsky

Throws on non-array seems to me a real reason for using "at". TypeScript is not everywhere yet and even it is, there's still "any".

 
ashleyo profile image
Ashley Oliver

You would implement a stack as an object? Trippy ...

Collapse
 
dsm637 profile image
Denis Mitropolsky

Another point - since everything in JS is an object, everything can be indexed using square brackets, not only array objects. So "at()" provides a safe way to get an element by index from the array and prevent an error when you're accidently working with someone which is not an array.

 
andrewbridge profile image
Andrew Bridge

Okay, fair point! Clarity is certainly an important factor in code (hence my jab at reduce!), and you are right, the whole Unix mantra is built on "do one thing well" and it seems to do okay!

I suppose at this point, just having something better than [arr.length - 1] would be a massive improvement, even if it were a little obscure. If at makes people happy over last great! My bet is now on the two specs opposing one another, then they'll both sit uselessly unadopted 🤦‍♂️

Some comments may only be visible to logged-in visitors. Sign in to view all comments.