18

In Python, you can do that:

arr = [1,2,3]
arr[-1] // evaluates to 3

But in JS, you can't:

let arr = [1,2,3];
arr[-1]; // evaluates to undefined

The question is: why?

I know about the tricks to get around it (arr[arr.length-1], modifying the array prototype, etc), but that is not the point.

I'm trying to understand why it is still not in the EcmaScript standards to interpret negative array indices as indices starting from the end, despite that it seems pretty easy to implement a JS engine that understands that (and also, the whole Python community is having a blast with this notation).

What am I missing?

Nino Filiu
  • 16,660
  • 11
  • 54
  • 84
  • 2
    Backward compatibility matters. – user2357112 Jan 06 '19 at 21:50
  • 2
    Because they didn't choose to make JavaScript work that way? – Herohtar Jan 06 '19 at 21:51
  • 1
    @Herohtar that's exactly my question - why did they make it that way – Nino Filiu Jan 06 '19 at 21:54
  • It makes more sense not to do it that way. Python is dictating that negative integer keys correlate with otherwise positive integer indexes. I think the assumption that this abstraction provides clarity is naive. – Rick Jan 07 '19 at 22:20
  • I updated [my answer](https://stackoverflow.com/questions/54066261/why-cant-i-do-array-1-in-javascript/64296726#5318303). With the new javascript `.at()` method, now, there is an official straightforward solution for this problem. Please see [my answer](https://stackoverflow.com/questions/54066261/why-cant-i-do-array-1-in-javascript/64296726#5318303). Maybe you want to accept it as the best answer. – Mir-Ismaili Oct 27 '21 at 11:19

4 Answers4

31

You miss the point, that arrays are objects (exotic object) and -1 is a valid key.

var array = [1, 2, 3];

array[-1] = 42;

console.log(array);
console.log(array[-1]);
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • That explains it. But is it a good programming pattern to use negative indices in arrays? I can't see any use that is not a hack – Nino Filiu Jan 06 '19 at 21:53
  • 1
    @NinoFiliu Javascript isn't known for being a language with lots of guardrails, rather for its simple, albeit flexible and expressive, semantics. It started as a scripting language to let people do things like animate their websites directly in the markup, in a time when everyone thought web applications would be written as Java applets. – juanpa.arrivillaga Jan 06 '19 at 22:07
  • 1
    @NinoFiliu If you're working with an array, using a negative index doesn't make sense, and you shouldn't do it. For example, [negative indices don't count toward the array length](https://stackoverflow.com/questions/13618571/should-negative-indexes-in-javascript-arrays-contribute-to-array-length). – Herohtar Jan 06 '19 at 22:25
25

As others said, In Javascript array[-1] is just a reference to a member of array indexed by "-1" (like "length") that is usually undefined (because array['-1'] is not evaluated to any value).

enter image description here

But there are other ways:

UPDATE 2021 (.at() method):

enter image description here

.at() method is now available in a wide range of environments:

  • Chrome / Edge / Chrome Android / WebView Android: v92+
  • Firefox / Firefox for Android: v90+
  • Opera: v78+
  • Node: v16.6.0 (V8 9.2) +
  • Deno: v1.12+
  • More info ...

Check if your browser supports it or not:

const array = [1, 2, 3]

try {
    console.log(array.at(-1))  // 3
    console.log(array.at(-2))  // 2
    console.log(array.at(-3))  // 1
    console.log(array.at(-4))  // undefined
} catch (e) {
    console.error("Sorry! Your browser doesn't support this feature yet!\nSee:\nhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/at#browser_compatibility")
}

The at() method takes an integer value and returns the item at that index, allowing for positive and negative integers. Negative integers count back from the last item in the array.

Can I use?


Use .slice(-N)[0]:

const array = [1, 2, 3]

console.log(array.slice(-1)[0])  // 3
console.log(array.slice(-2)[0])  // 2
console.log(array.slice(-3)[0])  // 1

In Strings you have another option (instead of [0]).

const string = 'ABC'

console.log(string.slice(-1))      // 'C'
console.log(string.slice(-2, -1))  // 'B'
console.log(string.slice(-3, -2))  // 'A'

Or using .substr(-N, 1):

const string = 'ABC'

console.log(string.substr(-1))     // 'C'
console.log(string.substr(-2, 1))  // 'B'
console.log(string.substr(-3, 1))  // 'A'
Mir-Ismaili
  • 13,974
  • 8
  • 82
  • 100
  • questionable efficiency, memory-wise.. `array.slice` creates a new array so you're potentially doubling memory consumption (worst case).. – Lloyd Oct 29 '20 at 15:21
  • 1
    Be aware that `Array.at` is still not supported on desktop or mobile Safari. – Isaac Moore Sep 08 '21 at 19:25
13

You can use arr[-1] - it will try to access the -1 property on the arr object, which is possible when weird code has assigned to the negative index. For example:

const arr = [1,2,3]
arr[-1] = 'foo';
console.log(arr[-1]);

Javascript property access has always worked this way - so, changing things so that [-1] will refer to the last item in the array would be a breaking change, which the standards strive very hard to avoid. (remember how they backed out of the Array.prototype.flatten name due to incompatibility with an extremely old and obsolete version of MooTools which still exists on only a few sites - this would be far worse)

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
4

Because most languages like the indexOf function to return -1 instead of a needless exception. If -1 is a valid index then following code would result in 3 instead of undefined.

var arr = [1,2,3]
console.log(arr[arr.indexOf(4)])

IMHO, Python made a mistake by make negative indexes valid, because it leads to many strange consequences that are not directly intuitive.

DragonAssassin
  • 929
  • 2
  • 10
  • 14
  • 1
    This is not the reason why. The array indexing heuristics were not designed to facilitate use of a specific function. If anything, the inverse is true - low-level behavior (like array indexing) was defined first and then functions were written within that ruleset. Reference: ["indexOf() was added to the ECMA-262 standard in \[ES5\]"](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf#polyfill) – rinogo Mar 04 '21 at 18:02
  • Also, indexOf() could return undefined or null just fine. – siride Mar 24 '22 at 23:03