DEV Community

loading...
Cover image for `at` coming soon to ECMAScript

`at` coming soon to ECMAScript

Laurie
Software dev at Netflix | DC techie | Conference speaker | egghead Instructor | TC39 Educators Committee | Girls Who Code Facilitator | Board game geek | @laurieontech on twitter
Originally published at laurieontech.com ・2 min read

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.

Discussion (91)

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
jfbrennan profile image
Jordan Brennan • Edited

Since at(-1) is the only interesting use case, why are we not talking about a last() function for Array? See Underscore/Lodash/Ramda.

I would really love to see arr[arr.length - 1] and arr.slice(-1)[0] go away with a new shiny fast last(). I'd also love a arr.lastIndex so I can stop doing arr.length - 1 in other situations.

Collapse
yoursunny profile image
Junxiao Shi

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

Collapse
jfbrennan profile image
Jordan Brennan • Edited

Yeah? And what if you need the third-from-last or fourth-from-last? My point is you don't.

99.99% of at(n) usage will be exactly at(-1) and we all know it, so let's do a last() thing already. There's more than a decade of precedence for it in the form of Underscore and Lodash and Ramda and jQuery and other libs, as well as several polyfills and requests to TC39. All of it culminated in a legit proposal. Devs have not only said they want it, but they do in fact use it!

TC39 should be more than willing to accommodate both. By all means, do at, but just know that two things will happen: the obvious at(-1) and then lots of totally pointless and verbose at(i) usage instead of [i] because at is "new" and [] is "old". Regardless, as long as the more desirable and cleaner and intuitive and useful and familiar last is added I'm happy.

jfbrennan profile image
Jordan Brennan • Edited

It's also worth considering that those libraries with "last" never bothered doing "at" even though they easily could have, but didn't because devs don't want/need it.

Collapse
laurieontech profile image
Laurie Author

Yup, that works fine

Collapse
laurieontech profile image
Laurie Author

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
jfbrennan profile image
Jordan Brennan

Thanks. I'm going to go save them from themselves ;)

Collapse
ivanjeremic profile image
Ivan Jeremic

I think getting last is not the only usecase, also I find .at(-1) clean and like that I can use the same function for all indexes.

Collapse
jfbrennan profile image
Jordan Brennan

But last() is cleaner still and is self-explanatory.

cmborchert profile image
Christopher Borchert

I agree, and so do a lot of people, but I think that there has been a long standing discussion about adding this feature to ECMA script and it just won't advance. It's been blocked at stage 1 for like 3 years or something...

ivanjeremic profile image
Ivan Jeremic

But last feels more something frameworks should do why should a language have last() I mean it is super easy to do .at(anyIndex)

jfbrennan profile image
Jordan Brennan • Edited

Please don't .at(anyIndex) because .at(positiveIndex) is the same thing as [].

Mark my words, we will see an explosion of at() as people stop using the now "old" [] in favor of at() simply because it's new. In 5 years time at() usage in the wild will breakdown like this:

  • 75% is pointless and verbose .at(positiveIndex) as some people stop []ing their arrays
  • 24.9% .at(-1)
  • 0.1% .at(otherNegativeIndexes)
  • and TC39 still won't have approved last()

God save us.

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.

jfbrennan profile image
Jordan Brennan • Edited

Passionate? No.
Patient - er, no longer patient? Yes :) The community has been asking for last() for 10 years.

As for at(-1) and last() being the same number of characters, that's not really what the community is after. It's this:

const el = arr[arr.length - 1]
const el = arr.slice(-1)[0]
const el = arr.at(-1)
const el = arr.last()
Enter fullscreen mode Exit fullscreen mode

Last is the only one which actually says what it does and it makes the developer's intention clear. If you are new to programming or new to JavaScript or even the English language, last() will also save you from confusion. To your second point - "[last] can only do one thing" - in software engineering that is widely accepted as good design. There are hundreds of existing features in JavaScript that can only do one thing.

jfbrennan profile image
Jordan Brennan • Edited

Oh yeah, the reduce overuse - you are so right! In my experience I see probably close to 50% of reduce usage being simple filtering and mapping and even basic loops 😥

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 🤦‍♂️

Collapse
jfbrennan profile image
Jordan Brennan

Please do not start making plans for all indexes. Only negative 🙏

Collapse
nickytonline profile image
Nick Taylor (he/him) • 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

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

Collapse
dzetah 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 (he/him) • 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 Author

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 Author

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 Author

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 Author

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

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
jfbrennan profile image
Jordan Brennan • Edited

That's all it would take isn't it? The problem with doing this though, and there's real cases of this happening, is later on when ECMAScript finally defines an official thing of the same name it breaks websites. It's one reason TC39 has hesitated to move on last(), but I say break the internet! Or just settle on a different name already and ship it!

Collapse
dewaldels profile image
Dewald Els

Break the internet! haha love it! Sometimes they need to take control and make the tough decisions for the betterment of the web.

Correct me if I am wrong, but if the official name is called .last(), the code above would simply overwrite it and not cause anything to break? However, if a library also creates a similar .last function, depending on the order of code execution, it could cause some unexpected results.

jfbrennan profile image
Jordan Brennan

Possibly. You may have 3rd party dependencies that rely on that native implementation behavior. There's also other potential issues that come from inheritance, which I think is the story behind smooshgate (google it).

Last I saw arr.lastItem was gonna be it. The problem though is for some inexplicable reason, somebody somewhere couldn't leave well enough alone and wanted it to be a setter too, which complicated things. It's hard to design an API for like a million(?) developers worldwide!

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
feichinger 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.

Collapse
milichev profile image
Vadym Milichev

I wonder will it work

const getLast = Array.prototype.at(?, -1); // see https://github.com/tc39/proposal-partial-application
const last = getLast({ length: 6, 5: 'last' });
Enter fullscreen mode Exit fullscreen mode

and do we really need it 🤔😆

Collapse
aleksandrhovhannisyan profile image
Aleksandr Hovhannisyan

Huh, TIL! I wonder why they wouldn't just extend the indexing operator to support negative indices. Seems easier than introducing a method, and now there's the question of which to use.

Collapse
stegriff profile image
Stephen Griffiths

The article covers this really:

Well, as it turns out, -1 is already a valid key. Arrays are really objects with indices as keys. So arr[-1] is looking at the arr object and the value of the "-1" key, which is undefined.

There can be a "-1" key present in the object, so the behaviour for "negative indices" is already defined by the language.

Collapse
laurieontech profile image
Laurie Author

Because it is already a valid index and would break existing code.

Collapse
aleksandrhovhannisyan profile image
Aleksandr Hovhannisyan • Edited

This is the part that I don't get, though. If arr[-1] returns undefined, then that means one of two things:

  1. Existing code checks to make sure indices are never negative.
  2. Existing code checks if arr[index] is truthy (or explicitly checks if it's undefined).

Either way, I don't see it breaking existing code if arr[-1] all of a sudden starts returning the last entry of an array. If we're doing #1, the API doesn't change for us since our code explicitly prohibits negative indexing. If we're doing #2, it still doesn't change because now negative indexing doesn't return undefined (unless the last element is, literally, undefined).

I only see this being an issue for actual objects. Can't arrays override the behavior of the indexing operator? (I'm not familiar with those kinds of low-level implementation details for JavaScript, so maybe not.)

laurieontech profile image
Laurie Author

"Unfortunately, JS's language design makes this impossible. The [] syntax is not specific to Arrays and Strings; it applies to all objects. Referring to a value by index, like arr[1], actually just refers to the property of the object with the key "1", which is something that any object can have. So arr[-1] already "works" in today's code, but it returns the value of the "-1" property of the object, rather than returning an index counting back from the end."

github.com/tc39/proposal-relative-...

aleksandrhovhannisyan profile image
Aleksandr Hovhannisyan

Oh, gotcha! So it would break existing code. Thanks for the reference!

Collapse
yoursunny profile image
Junxiao Shi

Coming from C++, my first feeling is that .at(nonexistent) would throw exception, but apparently this isn't the case.

Collapse
tiguchi profile image
Thomas Iguchi

I would also expect an exception to happen instead. Passing in an out of bounds index argument should be considered a misuse of the method and raise an error instead. The return result of undefined is not sufficient for detecting if the index is out of bounds, since that accessed element could be undefined.

In programming it's better to fail early, so come a crash and the time to debug, the failing line of code in the stacktrace is as close as possible to the root of the problem.

Ideally the language and API should make it harder to misuse it, but in JavaScript a lot of nonsensical things are possible that can cause hard to debug problems because cause and effect can be delayed. That forces us to come up with ways to make it stricter such as using static type checkers or switching to a different cross-compiled language altogether.

This could have been an opportunity for getting it right though 😞

Compatibility and interchangeability with the existing bracket syntax was probably not the reason behind that design decision. The at function expects an integer index argument. The official polyfill reference implementation converts non-numeric or fractional numbers to whole numbers, which is another strike in my book.

The method should simply reject non-numeric, non-integer arguments. Instead we end up with the following possibility that doesn't make sense to me either: a[1.1] !== a.at(1.1). Fractional numbers should cause a crash too. Why second-guess the programmer and try to auto-correct them based on assumptions? 🤷

Collapse
laurieontech profile image
Laurie Author

It returns undefined

Collapse
iorlas profile image
Denis Tomilin

What is quite a disappointing fact, instead of enhancing the language quality, addressing the code issues and simplifying the development, we see the new standards popping up with additions to the language, but keeping the old issues as well. Now "at" should be used instead of "[]", it makes a lot of sense, but will anyone learn that? Will the courses in popular websites address that? Nope.

It is clear they are trying to keep the backward compatibility, but enhancing the slicing operator would be so much better to the JS, developers, and the future of the dev in general.

Collapse
islamcn profile image
Isla McNeill

Idk, maybe it's because I'm a fairly new developer but, I'm actually really excited about this. It's just one more thing I can do to make my code more readable for others.

Collapse
dsm637 profile image
Denis Mitropolsky

Sure, it's considered a good writing style when you're using different ways of expressing the same thoughts. Every developer would loves that!

Collapse
jsnanigans profile image
Brendan Mullins

Why don't they just add support for arr[-1]. Seems like the simplest solution

Collapse
stegriff profile image
Stephen Griffiths

The article covers this really:

Well, as it turns out, -1 is already a valid key. Arrays are really objects with indices as keys. So arr[-1] is looking at the arr object and the value of the "-1" key, which is undefined.

There can be a "-1" key present in the object, so the behaviour for "negative indices" is already defined by the language.

Collapse
jsnanigans profile image
Brendan Mullins

Why would you ever need negative indexes that have their own value? This sounds like a big in the language to me

stegriff profile image
Stephen Griffiths • Edited

¯\(ツ)/¯ Any string is a valid key in a javascript object

Collapse
laurieontech profile image
Laurie Author

I believe I explained that in the post. It’s already a valid key.

Collapse
jsnanigans profile image
Brendan Mullins

Sure, but does it make sense that -1 is a key? That actually sounds like a bug to me xD instead all negative numbers could be used as reverse indexes instead of looking for a value in the array where there really should not be any.

Collapse
kaspermroz profile image
Kasper Mróz

I guess to implement negative index they would have to re-implement arrays? 🤔 As arrays are of type Object.

Collapse
richardeschloss profile image
Richard Schloss

I was sorta hoping the function was going to be for the Date object too :)

Date.at([some UTC time], () => { /* do stuff */ })

Collapse
alexjoeb profile image
AlexJoeb

I'd love to see functionality similar to python's arr[1:] come out of this. But, this will definitely be useful!

Collapse
nicholaschiasson profile image
Nicholas Omer Chiasson

Does this work with positive out of bounds also, kind of like a modulo of the length? Say arr.length is 4 and you tried arr.at(5), does it give you arr[1]? Just curious.

Collapse
islamcn profile image
Isla McNeill • Edited

It will give you undefined. That way you're not running yourself in circles.

Collapse
laurieontech profile image
Laurie Author

No. Positive out of bounds is undefined.

Collapse
yoursunny profile image
Junxiao Shi

Modulo is risky because you get NaN on an empty array.

Collapse
rosostolato profile image
rosostolato

It will be useful if it can access not just last but also the length - k item with arr.at(-k). For example arr.at(-3). If it's just for the last one, it won't be that good.

Collapse
laurieontech profile image
Laurie Author

Yes, any negative index works.

Collapse
gcsboss_ profile image
Guilherme C. Souza

arr.last(1) <--

Collapse
ajozz13 profile image
Alberto Ochoa • Edited

Why not array.first and array.last?

Collapse
laurieontech profile image
Laurie Author

Those proposals were abdanoned in favor of this because it allows first and last and anything else.

Collapse
vladimirschneider profile image
vladimirschneider

I think this is interesting only for getting last element of array but I will not use .at for something other

Collapse
kalashin1 profile image
Kinanee Samson

They should also allow us to slice an array backwards

Collapse
ivanjeremic profile image
Ivan Jeremic

JavaScript needs an official compiler which is maintained by ECMA. That way instantly release new features and it will work when compiling.

Collapse
laurieontech profile image
Laurie Author

Any stage 4 proposal needs to include a Babel plugin. So Babel can act as this.

Collapse
pris_stratton profile image
pris stratton

Haskell laughs at JS’s array methods 😀

Collapse
developerbryan profile image
developerbryan

Any idea why the -1 key is a valid key in arr[-1], but undefined?

Collapse
sharpninja profile image
The Sharp Ninja

LINQ should be completely implemented in JS, Rust and every other language looking to grow/keep market share.

Collapse
jloa profile image
Julius • Edited

Seems like another bs from tc39.
Their marasmus and laziness amaze me moar everyday.
Relative to the subject -- the only usecase of this new syntax is at(-1).
Therefore the proposal is bs. The proper proposal would be implementing Array.prototype.last() method like in other languages

Collapse
jfbrennan profile image
Jordan Brennan

I wouldn't call it BS.

Not all, but some of them are volunteers giving up personal time to do the hard work of defining the spec for new features and writing the test cases and all that. As such they're naturally going to devote their time on stuff they favor, which means if you are part of TC39 and you like Python's negative indexing feature, you're going to get excited about bringing that to JavaScript and put your energy into that, even though the community isn't really asking for it. And that's what's happening here with at(). Add in the fact that TC39 views at(-1) as a sufficient compromise to the extremely popular and long-standing request for last() and now you see why at() is being ushered through.

I don't envy the folks involved - it's a hard job - but I am disappointed in the outcome here.

Collapse
jloa profile image
Julius

Totally agree with u. Feel the pain.