Javascript Language feature Idea

# RacerD123 (9 years ago)

In other programming languages such as python, you can specify the last element of a list with "list[-1]" but in javascript you have to do "list[list.length-1]". Is there maybe a way to make this feature in javascript?

# Sander Deryckere (9 years ago)

Since Array inherits from Object, it can have any key, including the key "-1". So something like list[-1] would break compatibility as users can now already assign values to the index -1.

If you want a short way to access the last element, it should probably be a function in the Array prototype. Something like list.last().

, Sander

2016-01-22 18:53 GMT+01:00 RacerD123 <racerd123 at gmail.com>:

# Jeremy Martin (9 years ago)

Don't forget that Array#slice() can accept negative indexes. E.g.:

['a', 'b', 'c', 'd'].slice(-1) [ 'd' ]

Which is nearly as terse as any Array.prototype addition would be, and I think it's at least expressive enough to alleviate the need for one.

# Dmitry Soshnikov (9 years ago)

On Fri, Jan 22, 2016 at 9:53 AM, RacerD123 <racerd123 at gmail.com> wrote:

In other programming languages such as python, you can specify the last element of a list with "list[-1]" but in javascript you have to do "list[list.length-1]". Is there maybe a way to make this feature in javascript?

This could be a good addition, although slice may work as well. This has been discussed previously as well in the last 4-5 years. In addition with non-enumerable properties you can just monkey-patch Array.prototype with the peek or last method. I guess approach when some library forces some method to widely used, can be a good candidate for adding it to the spec (this how it was with Function.prototype.bind, e.g., although today monkey-patching is a bad practice of course).

In addition, you can monkey-patch just -1, and other indexes, e.g. a gist from 5 years old, when a similar topic was proposed here: gist.github.com/DmitrySoshnikov/984921

Dmitry

# Bradley Meck (9 years ago)

Personally I prefer a well known symbol for marking you are grabbing from the end of the list rather than this wrapping behavior like D ( dlang.org/spec/arrays.html#array-length ). That said I think .last is more reasonable than changing prototypes.

# Alican Çubukçuoğlu (9 years ago)

More cool stuff:

const arr = [1, 2, 3];

// Easy push
arr[] = 4; // => arr[arr.length];

// Easy s(p)lice
arr[begin, end];,
arr[begin,]; // => arr[begin, arr.length - 1];

arr[begin, end] = [1, 2, 3];

A terrible example (terrible because this should be done with WebGL shaders):

const image = [ /* Umbagajillion of RGBA pixels */ ];

function manipulate(rgba) {
  rgba[0] += 10;
  rgba[1] += 10;
  rgba[2] += 10;
}

for (let i = 0; i < image.length / 4; i++) {
  const begin = i * 4;
  const end = begin + 4;

  /*
    In case easy s(p)lice doesn't actually Array.p.slice
    and just creates a limited view of the array
    without breaking reference
    (image[begin, end] === image[begin, end])
  */
  manipulate(image[begin, end]);

  /*
    In case easy s(p)lice does Array.p.slice
    and creates a new array
    (image[begin, end] !== image[begin, end])
  */
  const pixel = image[begin, end];
  manipulate(pixel);
  image[begin, end] = pixel;
}

Edit: Fixed the typo @kdex pointed out. Thanks!

# kdex (9 years ago)

I really adore the slicing syntax. Just as a quick fix, though,

arr[begin,]; // => arr[begin, arr.length];

should obviously return arr[begin, arr.length - 1].

Honestly, I'm not too sure if the "easy push" syntax is particularly self- explanatory. There might be better alternatives.

# kdex (9 years ago)

Standardizing unused array keys breaks compatibility about as much as extending the prototype does, really. Users can already mangle with both.

The problems are a little more subtle, and yes, it would severely break backwards compatibility. Consider this example (accessing an element based on a computed index):

let a = [1, 2, 3];
let occurrence = a.indexOf(4);
a[occurrence];

Currently, this should result in undefined. After your proposal, you could introduce a potential bug into every program that accesses arrays using an index computed with Array.prototype.indexOf, since it would now return 3.

# Benoit Marchant (9 years ago)

Interesting! More pedestrian, it would be nice to finally have a version of splice that doesn't create a new other Array as a side effect.

# Alican Çubukçuoğlu (9 years ago)

The "easy push" syntax exists in PHP so it will be familiar to at least some people. The slicey thing was also being discussed for PHP. I don't know if it landed or got dropped.

# John Gardner (9 years ago)

Using a well-known symbol to access an array's last element is probably wiser than a typical method or property:

let a = [0, 1, 2, 3]; console.log( a[Symbol.last] === 3 /* true */ );

There're obviously instances where authors have extended Array prototypes with "last" methods or properties, but we can't guarantee they'd all work the same. For instance, assume there are some implementations that skip undefined values:

var a = [0, 1, 2, undefined, undefined]; Array.prototype.last = function(){ return this[this.length - 1]; }; /** One that skips undefined values */ Array.prototype.last = function(){ var offset = 1; while(offset < this.length && undefined === this[this.length - offset]) ++offset; return this[this.length - offset]; }

These discrepancies are subtle, but have the potential to break backwards compatibility.

Using a well-known symbol eliminates the potential for conflict. Furthermore, it also offers an opportunity to complement any iterable object's ability to synthesise array-like behaviour. For instance, it enables iterables to also return the last object in their list of values:

let pruebas = { data: ["Probando", "la", "mierda", "esta", undefined],

Symbol.iterator{ /* Stuff with .data / }, Symbol.last{ /* Stuff to skip undefined values or whatever */ let offset = 1; let data = this.data; while( offset < data.length && undefined === data[data.length - offset] ) ++offset; return data[data.length - offset]; } }

# kdex (9 years ago)

While John's solution doesn't run into conflicts with downward compatibility, it still wouldn't solve the problem of getting the n-th last element of an array. To solve this, it'd probably be a good idea to extend the prototype and specify a parameter, defaulting to 1.

Array.prototype.last doesn't show up a terrible lot on search engines, either, so we might actually be lucky here. Other than that, I also found two more threads[1][2] on the EcmaScript discussion archives that propose it.

They might be worth a read.

[1] esdiscuss.org/topic/array-prototype-last [2] esdiscuss.org/topic/proposal

# Thomas (9 years ago)

Is this what you're thinking?

Array.prototype.nth = function (n){ if(n < 0){ return this[this.length -n]; } else { return this[n]; } }

Thomas Foster

@thomasfoster96 Ph: +61477808008 thomasfoster.co

# /#!/JoePea (9 years ago)

I think Symbol.last seems like the best option so far, without breaking things.

# John Gardner (9 years ago)

Thomas is right. "last" should unambiguously mean last. If authors want to ascertain the second-last, or third-last, a different method is probably preferable. "nth" sounds great, and could work from the end of the array if passed a negative value.

This would circumvent the issues raised by overriding the -1 property a.l.a Python.

# kdex (9 years ago)

@Thomas: I think your implementation has a bug for negative values of n. It should have been + n instead of -n, probably.

To me, It would feel a little more "natural" if it behaved somewhere around this:

/* Used like the [] operator, but indices are counted from right to left and 
also works with negative numbers */
Array.prototype.last = function(n) {
	if (n === undefined) {
		n = 0;
	}
	else if (typeof n !== "number") {
		throw new Error("First argument must be a number");
	}
	if (n < 0) {
		return this[Math.abs(n) - 1];
	}
	return this[this.length - 1 - n];
};
/* Used like the [] operator, but also works with negative numbers */
Array.prototype.nth = function(n) {
	if (n === undefined) {
		n = 0;
	}
	else if (typeof n !== "number") {
		throw new Error("First argument must be a number");
	}
	if (n < 0) {
		return this.last(Math.abs(n) - 1);
	}
	else {
		return this[n];
	}
};
[1, 2, 3].last();     // 3
[1, 2, 3].last(0);    // 3
[1, 2, 3].last(1);    // 2
[1, 2, 3].last(2);    // 1
[1, 2, 3].last(3);    // undefined
[1, 2, 3].last(-1);   // 1
[1, 2, 3].last(-2);   // 2
[1, 2, 3].last(-3);   // 3
[1, 2, 3].nth();      // 1
[1, 2, 3].nth(0);     // 1
[1, 2, 3].nth(1);     // 2
[1, 2, 3].nth(-1);    // 3
[1, 2, 3].nth(-4);    // undefined
[1, 2, 3].nth(null);  // error
[1, 2, 3].last(null); // error
# Bob Myers (9 years ago)

On Sat, Jan 23, 2016 at 12:54 PM, kdex <kdex at kdex.de> wrote:

[1, 2, 3].last(); // 3

I'm wondering what the ultimate motivation for suggestions like this is. Is it to save key strokes? Allow more semantic coding? Support new paradigms? Performance? 'm sure someone has already come up with a good categorization like this, would someone mind providing a link? Could one of these be considered the "theme" for the next version?

I have to admit to be being quite negative about proposals of the form "Hey, this other language does X, can we do that too?", or "It would be so cool if we could do Y", especially when these are mostly about syntax. Is a missing last really one of our painpoints?

Bob

# kdex (9 years ago)

Not every feature addition is due to performance or paradigms. Just have a look at ES2015: I'm sure that this has neither been the motivation for String.prototype.startsWithnor for String.prototype.includes. Even String.prototype.repeat appears so simple that a loop paired with a concatenation could have become a popular alternative.

Of course you could solve most of these string problems with earlier versions of the language, too, often explicitly thinking with incides. But on the downside, your code suddenly becomes a potentially unintuitive, index-ridden mess, introducing off-by-one and out-of-bound errors (it even happened to someone on this thread, too, if you review Thomas' code from above). This isn't really all too much about saving keystrokes, but mainly about writing clean, readable and maintainable code.

There's array::back in C++, negative indices in Python as well as Bash or end in PHP, so I don't see any reason why we should complicate things for people coming from these languages. Nor to I see why we should torture ourselves thinking about how the underlying data structure stores its elements internally when all I care about is reading the last element.

Just ask yourself: Do you think it's substantially more readable to write

[1, 2, 3].slice(-2)[1];

over

[1, 2, 3].last(1);

?

If it comes to write access, I agree thatSymbol.last could be another handy addition (it doesn't have to be an "either/or" discussion, really):

[1, 2, 3][Symbol.last]; // 3 [1, 2, 3][Symbol.last] = 4; // 4 [1, 2, 3].last(1); // 2

# Michał Wadas (9 years ago)

I can't consider .last(1) method to be readable... But I think .nth(n) method with support for negative index would be really useful.

2016-01-23 17:32 GMT+01:00 kdex <kdex at kdex.de>:

# kdex (9 years ago)

Just a short note:

I was not aware that it was an explicit design goal to simplify things for people coming from other languages.

Well, it most certainly was. That's why we're "blessed" with ASI. :)

# kdex (9 years ago)

@Michał: That really depends on the point of view: If you need zero-based indexing from the right, as [] does from the left, you'd use Array.prototype.last.

# /#!/JoePea (9 years ago)

Freedom of choice for the win. I like it.

# Garrett Smith (9 years ago)

On Sat, Jan 23, 2016 at 12:46 PM, kdex <kdex at kdex.de> wrote:

@Michał: That really depends on the point of view: If you need zero-based indexing from the right, as [] does from the left, you'd use Array.prototype.last.

On Samstag, 23. Januar 2016 20:56:02 CET Michał Wadas wrote:

I can't consider .last(1) method to be readable... But I think .nth(n) method with support for negative index would be really useful.

you can also slice from the end of an array:

var a = [1,2,3,4]; // Get the second-to-last item. a.slice(-2,-1)

A method to find an item at a given index could be designed.

a.itemAt(-2);

I trimmed the rest. I have enough difficulty with advanced GMail view. I can't get back to Basic HTML view as default. Top posting makes editing a lot harder in GMail Advance View. Plus it auto-saves on every undo/redo, and coupling that with El Capitan, and it's all so slow and clunky.

So I didn't fix yuour top post completely. Because I'm saving my energy up for figuring out how to paste into youtube comments. Man, this web stuff, google… so cutting edge. Wow!

# Gary Guo (9 years ago)

I oppose the Symbol approach. It makes code looks ugly just for adding write access. There is already a .push(), and we only need to add .last() or .back() to make it complete.

# Andrea Giammarchi (9 years ago)

Array.prototype.nth(n=0) looks great indeed, +1 here

About the Symbol ... ugly as hell also we need to write even more and it doesn't scale as utility compared to .nth

a[Symbol.last]
a[a.length-1]

I mean, seriously ... don't even consider that or someone might ask to implement Symbol.first with second, third and all others too ... how about Symbol.odd /sarcasm

Best

# Caitlin Potter (9 years ago)

Nitpicking here, but the nth method is traditionally named at, if it were going to be formally proposed

# Andrea Giammarchi (9 years ago)

FWIW .at works for me. Anything really, as long as Symbol.last won't even be proposed :D

# Waldemar Horwat (9 years ago)

On 01/25/2016 12:00, Andrea Giammarchi wrote:

Array.prototype.nth(n=0) looks great indeed, +1 here

About the Symbol ... ugly as hell also we need to write even more and it doesn't scale as utility compared to .nth

a[Symbol.last]
a[a.length-1]

I fail to see the point of this, other than trying to increase the complexity of the language by adding even more cases which do the same things but work somewhat differently from existing cases.

We'd have done a lot of things differently if we were starting from scratch. But arrays have a large amount of legacy behavior we can't realistically change and, given that, this doesn't improve things much.

 Waldemar
# Bob Myers (9 years ago)

The syntactical construct array() is unused as far as I know, since currently it would always generate a syntax error--an array is not a function. Thus we can use this as in

[1,2,3](-1)

Shouldn't break anything.

Bob

# /#!/JoePea (9 years ago)

1,2,3

But it implies a function call, which isn't the case. It could possibly be confusing when reading code.

# Bergi (9 years ago)

Bob Myers schrieb:

The syntactical construct array() is unused as far as I know, since currently it would always generate a syntax error--an array is not a function.

It's a runtime TypeError rather.

Thus we can use this as in

[1,2,3](-1)

Shouldn't break anything.

This would break much more than a last getter/method or nth/at method. Arrays becoming callable would mean that their typeof changes from "object" to "function", which is a very bad idea.

, Bergi

# Andrea Giammarchi (9 years ago)

Not sure if you are talking about Array.prototype or Symbols but whatever worry you have is identical. Having a Symbol in the prototype or adding a method ... I don't see much difference. Which one is best? History shows it's the method. It plays well, it's easy to polyfill, no complexity added ... is just an Array.prototype.method.

We can survive witohut the .at or .nth like we've done long time, but if a library would add such prototype I won't blame it. Prototype and others made most of ES5, after all, and I think for good, common, useful, use cases.

I'm also off this conversation since I don't have any strong need for such method.

# Bob Myers (9 years ago)
[1,2,3](-1)

I will not beat this dead horse further, and it may be visually undesirable, and/or hard to implement, but for what it's worth, the idea is not to make arrays callable; it's just new syntax to allow parenthesized expressions following arrays, which would be interpreted as an index whose value to extract, with negative values meaning to go from the end.

# Jonas Sicking (9 years ago)

On Mon, Jan 25, 2016 at 12:38 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

FWIW .at works for me. Anything really, as long as Symbol.last won't even be proposed :D

If we name it .item that would mean that a whole bunch of DOM classes could be replaced with plain JS Arrays.

For example FileList, MessagePortList, DOMRectList, TouchList, etc.

It would also mean API compatibility with a whole lot of more APIs, like NodeList, DOMTokenList and CSSValueList. While we couldn't replace these with actual Arrays (for varying reasons), we'd end up with classes that have more API surface in common.

/ Jonas

# Tab Atkins Jr. (9 years ago)

On Tue, Feb 2, 2016 at 12:15 PM, Jonas Sicking <jonas at sicking.cc> wrote:

On Mon, Jan 25, 2016 at 12:38 PM, Andrea Giammarchi <andrea.giammarchi at gmail.com> wrote:

FWIW .at works for me. Anything really, as long as Symbol.last won't even be proposed :D

If we name it .item that would mean that a whole bunch of DOM classes could be replaced with plain JS Arrays.

For example FileList, MessagePortList, DOMRectList, TouchList, etc.

It would also mean API compatibility with a whole lot of more APIs, like NodeList, DOMTokenList and CSSValueList. While we couldn't replace these with actual Arrays (for varying reasons), we'd end up with classes that have more API surface in common.

I love a good bird-killing stone. ^_^ Let's do this!

# Axel Rauschmayer (9 years ago)

Another possibility is Array.prototype.get(), which would be in line with ES6 Maps.

# Biju (9 years ago)

We cam make this simpler, in Javascript Array.slice() already accept negative index. Developers from Javascript and other languages are familiar with negative value for index parameter. So why cant we make array accept negative index.

in comparison with Bob Myers' proposal

[1,2,3](-1)

will become

 [1,2,3][-1]

This will also allow us to set value using negative index, example

var array_data = [1, 2, 3, 4]; array_data[-1] = 5; array_data[-3] = 6;

// now array_data is [1, 6, 3, 5]

# Frankie Bagnardi (9 years ago)

That would break backward compatibility;

var a = ['a'];
a['-1'] = 'test';
Object.keys(a) // ['0', '-1']
# Biju (9 years ago)

On 17 April 2016 at 17:29, Frankie Bagnardi <f.bagnardi at gmail.com> wrote:

That would break backward compatibility;

var a = ['a'];
a['-1'] = 'test';
Object.keys(a) // ['0', '-1']

Do we have statistics how many sties depend on that?

# /#!/JoePea (9 years ago)

Backwards compatibility has been broken before. I don't think this one is too bad of a breakage.

# kdex (9 years ago)

I don't see a good reason why to mangle with this. Note that you can achieve this behavior without breaking backwards compatibility with ES6 Proxies:

class MyArray extends Array {
	constructor(...args) {
		super(...args);
		function computeProperty(target, property) {
			const index = +property;
			return index < 0 ? String(target.length + index) : property;
		}
		return new Proxy(this, {
			get(target, property, receiver) {
				return Reflect.get(target, computeProperty(target, property), receiver);
			},
			set(target, property, receiver) {
				return Reflect.set(target, computeProperty(target, property), receiver);
			}
		});
	}
}
# /#!/JoePea (9 years ago)

But, can

let a = [1,2,3]

create a new MyArray? Maybe, instead of having negative indices by default (which breaks some backwards compatibility) we can introduce a symbol for overriding property access? Something like

Array.prototype[Symbol.propertyAccess] = function(index) {
  if (index < 0) ...
  else ...
}

? Just an idea; I'm not sure if that's a good use for Symbols. We could then easily add this helper code to a given app.

# kdex (9 years ago)

Yes, now we're heading in the right direction.

The problem with something like Symbol.propertyAccess is that this might lead to a flood of new well-known Symbols. Conceptually, Symbol.propertyAccess sounds like it should have been a Proxy trap, anyway.

Here's an more general idea: Why not allow users to set a derived class for literals via well-known Symbols? Thus, users could provide custom implementations for RegExp, Array, Object (…) literals, as long as the value points to a derived class.

We could even introduce negative array indices in a way that doesn't break the web like this:

[1, 2, 3][-1]; // undefined
Array[Symbol.implementation] = MyArray;
[1, 2, 3][-1]; // 3
Array[Symbol.implementation] = 3; // TypeError: Array implementations must extend Array (→ Array.isPrototypeOf(Number(3)) is false)
# Andy Earnshaw (9 years ago)

That would mean all other programs executing on the page would be forced to use that Array implementation, imposing potentially critical problems with, for example, performance and expected behavior. It's just not a good idea.

I missed off esdiscuss when I replied earlier, but I mentioned that the only reasonable solution is to introduce new syntax, e.g.

myArray[:-1]

However, it's been said that there needs to be a compelling reason to add new syntax and I'm not sure this qualifies imo.

# /#!/JoePea (9 years ago)
Array[Symbol.implementation] = MyArray;

That would mean all other programs executing on the page would be forced to use that Array implementation

And also with my suggestion that would impact all code too.

Would it be possible to limit the effect of using certain symbols to a scope where the symbol is used? For example:

function main() {
  Array[Symbol.implementation] = MyArray;

  let a = [1,2,3] // uses MyArray
}
let a = [1,2,3] // uses Array
main()

or

Array[Symbol.implementation] = MyArray;
function main() {
  let a = [1,2,3] // uses MyArray, from outer scope
}
let a = [1,2,3] // uses MyArray
main()

Or maybe some other method on a per-scope basis?

# Andy Earnshaw (9 years ago)

I don't think that would be trivial to implement and there might not be a common enough use case for it. You might want to look into something like sweetjs.org if it's the syntactic sugar you're looking for.

# kdex (9 years ago)

Symbol.implementation should be fairly trivial to implement once Realms are around, without affecting global scope.

# Michael Theriot (9 years ago)

This can be trivially done with proxies. I don't think arrays should have a special trap dependent on whether or not a symbol is set. If I were to make a custom array with proxies I would then have to check if someone changed standard behavior by inserting a symbol, and pass the symbol to the target just to work properly.

If the var arr = [1,2,3]; syntax is the real reason to change default behavior then just use Array.from like such var arr = InvertedArray.from([1,2,3]);. See below...

var getProp = (target, property) => typeof property !== 'symbol' && target.length && property < 0 && property >= -target.length
  ? +property + target.length
  : property;

var handler = {
  get: (target, property, receiver) => Reflect.get(target, getProp(target, property), receiver),
  set: (target, property, value, receiver) => Reflect.set(target, getProp(target, property), value, receiver)
};

var InvertedArray = new Proxy(function InvertedArray(arg1) {}, {
  construct: (target, arguments, newTarget) => new Proxy(Reflect.construct(Array, arguments, InvertedArray), handler)
});

Reflect.setPrototypeOf(InvertedArray, Array);
InvertedArray.prototype = Object.create(Array.prototype);

Arrays are effectively regular objects with traps on get/set to update the length property. I don't think they should have a special new syntax to access negative indices like arr[:-1] since it would logically have to work on all objects and is dependent on a length property; it's just syntactic sugar for yet another trap.

You can also make a callable array without the typeof issue...

var getProp = (target, property) => typeof property !== 'symbol' && target.length && property < 0 && property >= -target.length
  ? +property + target.length
  : property;

var handler = {
  apply: (target, thisArg, arguments) => Reflect.get(target, getProp(target, arguments[0]))
};

var CallableArray = new Proxy(function CallableArray(arg1) {}, {
  construct: (target, arguments, newTarget) => new Proxy(Reflect.construct(Array, arguments, CallableArray), handler)
});

Reflect.setPrototypeOf(CallableArray, Array);
CallableArray.prototype = Object.create(Array.prototype);

... but you can't set anything with the arr(-1) syntax obviously.

# Andy Earnshaw (9 years ago)

I assume if you were to spec out the additional syntax you'd go the whole hog and add ranges, e.g.

arr[:-4, -2];

Like I said, not particularly compelling as it's not a huge saving over slice() and selecting single negative indices can be done with Proxy.

# Bob Myers (9 years ago)

Although there seems to have been a decisive lack of interest in my extended pick notation proposal, it cleanly subsumes the entire idea of "lastElement" and its endless rehashed variations involving new prototype methods or symbols or whatever else people proposed.

In my proposal, "lastElement" is just

array.-1

An arbitrary expression can follow the dot, so

index = -1;
array.(index);

As far as I am aware, no one has said this is bad, or unparseable, or backward incompatible, just that it is excessive and gratuitous and unnecessary sugar.

If extending the dot to take expressions to the right is considered aesthetically undesirable for whatever reason--personally, to me, it seems completely intuitive--then at the cost of eating up a precious special operator, the proposal suggests the alternative of the pound sign:

array # -1

This proposal does much more than just let you take the last element of an array. As a trivial example, an alternative way to reverse an array falls out of it naturally:

array.[-1 to 0]

And, it also provides a way to build new objects from other objects' properties, which was the motivation for the whole thing.

object.{a, b}

Take another look at it if you're interested.

Bob

# Bruno Jouhier (9 years ago)

Why new syntax here? This is just a method that's missing: array.last(n).

array[-1] would break things and it is difficult to find out where. Consider:

for (var i = array.length - 1; array[i]; i--) doSomething(array[i]);

Not the best way to write such a loop but changing array[-1] would break it.

# Bob Myers (9 years ago)

Please go back and read the existing threads. We've been over this territory ad nauseum.

Yes, of course array[-1] would break the web and that's why no one is seriously proposing that.

As already discussed extensively, Array#last may be an option, but it could collide with libraries or other code which extends Array.prototype, and they seem too specialized (why not also lastButOne?).

Bob

# Alexander Jones (9 years ago)

This is why we can't have nice things... Literally anything we do with the language at all can collide with libraries. Introducing new syntax? Code that was using eval and throwing syntax errors is now behaving differently and you 'broke the web'... Adding a Symbol.last method to Array#? People who were abusing Arrays as property bags might have code that was adding that key as a property already and you 'broke the web'...

If this is seriously the attitude we're taking as a community, we should stop wasting our time and just accept that WebAssembly is the only reasonable way forward. All of these modern nice-to-haves are a crutch and a distraction from this and obfuscate the need for a real solution.

Of course, we cleaned up parts of the language once already with 'use strict'. I don't see why we can't do it again once or twice a decade...

Alex

# Bruno Jouhier (9 years ago)

Adding last to Array.prototype is no different than adding endsWith to String.prototype. ES2015 did it.

It won't break most existing code that defines Array.prototype.last. Such code will just overwrite the new last method and will continue to work as before.

You won't be able to mix old libraries that have their own last method with new code if the methods behave differently but that's a different story; that's not breaking existing web pages.

What will break though is existing code that test the existence of Array.prototype.last; but String.prototype.endsWith had the same issue.

last(1) would return last but one, and last() would be the same as last(0)

# Tiddo Langerak (9 years ago)

If we're talking about adding a new method anyway, why not just add a more general get method with support for both positive and negative indices? That would be consistent with the other array methods, and additionally I think it is more natural to use arr.get(-2) than arr.last(1). Using last to get anything but the very last element just seems weird to me.

# Jordan Harband (9 years ago)

Let's not cry doomsday because there's not an easy path to adding "-1" array access, when indexing into a list of things in the first place is already a potential code smell.

endsWith was able to be added because it didn't break anything. contains was renamed to includes because the former did break something. There's lots of examples of this principle at work. New things can be added any time they don't break the web. Nothing will ever be knowingly added that does break the web. Please accept that. Creating a new web language won't necessarily "solve" this "problem" - future iterations of that language would almost certainly still be subject to the overriding principle of not breaking the web.

I think Array#get/Array#set are the most reasonable suggestions offered so far, but I don't think the added value will be enough to convince the committee that it's worth adding to the already large API surface of the language - and the asymmetry with the similar methods on Map/Set would be problematic (note that Set#get(-1) does not retrieve the last item in the set, for example).

# Michał Wadas (9 years ago)

2016-04-19 10:40 GMT+02:00 Jordan Harband <ljharb at gmail.com>:

(note that Set#get(-1) does not retrieve the last item in the set, for example).

Because Set.prototype.get doesn't even exist. And Set by definition is an unordered collection, so there is no "last" element.

I think Array.prototype.get and Array.prototype.set are the way to go - it should not break backward compatibility. And it's consistent with Array.prototype.slice behaviour.

BTW, this was discussed 3 years ago: esdiscuss.org/topic/array

# Michael Theriot (9 years ago)

And Set by definition is an unordered collection, so there is no "last" element.

Sets in ES6 are iterated in insertion order.

I don't think a .get or .set needs to be made for Array. Why would you use arr.set(1, 4) over arr[1] = 4, and why should there be more ways of doing the same thing? It would only ever be used for negative indices, and Reflect.get/Reflect.set already exists without that behavior.

The original question was to use negative indices, which you can do with proxies. If a method should be added I think .last is the safest and would expect any implementation of it to have the same behavior.

An array[Symbol.last] getter/setter would also be safe. You can even add it yourself without collision.

var last = Symbol();

Reflect.defineProperty(Array.prototype, last, {
  get: function () {
    return Reflect.get(this, this.length - 1);
  },
  set: function (value) {
    return Reflect.set(this, this.length - 1, value);
  }
});

var arr = [1,2,3];
arr[last] = 0;
console.log(arr); // 1,2,0

Maybe a .last (string) property getter/setter would also work here.

# Allen Wirfs-Brock (9 years ago)

On Apr 19, 2016, at 1:40 AM, Jordan Harband <ljharb at gmail.com> wrote: ... I think Array#get/Array#set are the most reasonable suggestions offered so far, but I don't think the added value will be enough to convince the committee that it's worth adding to the already large API surface of the language - and the asymmetry with the similar methods on Map/Set would be problematic (note that Set#get(-1) does not retrieve the last item in the set, for example).

A different approach that addresses a broader set of use cases is described in web.archive.org/web/20141230040303/http://wiki.ecmascript.org/doku.php?id=strawman:object_model_reformation, web.archive.org/web/20141230040303/http://wiki.ecmascript.org/doku.php?id=strawman:object_model_reformation

Of course, that proposal is quite out of date (“private names” instead of Symbols, etc.) and would require some significant updating to the current state of the language.