163

In coffeescript this is straightforward:

coffee> a = ['a', 'b', 'program']
[ 'a', 'b', 'program' ]
coffee> [_..., b] = a
[ 'a', 'b', 'program' ]
coffee> b
'program'

Does es6 allow for something similar?

> const [, b] = [1, 2, 3]                              
'use strict'                                           
> b  // it got the second element, not the last one!                      
2                                                      
> const [...butLast, last] = [1, 2, 3]          
SyntaxError: repl: Unexpected token (1:17)                                                                                                                                                        
> 1 | const [...butLast, last] = [1, 2, 3]                                                                                                                                                        
    |                  ^                                                                                                                                                                          
    at Parser.pp.raise (C:\Users\user\AppData\Roaming\npm\node_modules\babel\node_modules\babel-core\node_modules\babylon\lib\parser\location.js:24:13)                                           

Of course I can do it the es5 way -

const a = b[b.length - 1]

But maybe this is a bit prone to off by one errors. Can the splat only be the last thing in the destructuring?

George Simms
  • 3,930
  • 4
  • 21
  • 35
  • 34
    @FelixKling the question is in particular about the behavior of `...` in es6, particularly that it can only be used as the last thing when destructuring or in a parameter list. This is potentially counterintuitive to someone coming into es6 from coffeescript and thus this question is potentially useful. – George Simms Oct 12 '15 at 18:28
  • 1
    That means besides `[1,2,3].slice(-1)` you even can't destructure equivalent to `[1,2,3].slice(0, -1)`. These are common operations. ES6 destructuring is somehow a joke! –  Jun 04 '16 at 06:04
  • @Iven there is a legitimate reason - to handle infinite iterables. – George Simms Jun 16 '16 at 05:20
  • Too bad rest as first parameter doesn't work. Would have been cool... Here is a jsperf using some of the answers https://jsperf.com/destructure-last/1 – Shanimal Aug 11 '17 at 14:30
  • 2
    There’s a [TC39 proposal called _Double-Ended Iterator and Destructuring_](https://github.com/tc39/proposal-deiter) that links to this question. – Sebastian Simon Oct 19 '20 at 16:06
  • what's wrong with `const [c] = ['bee'].slice(-1)`? – Andre Figueiredo Aug 17 '21 at 20:27

18 Answers18

264

console.log('last', [1, 3, 4, 5].slice(-1));
console.log('second_to_last', [1, 3, 4, 5].slice(-2));
dota2pro
  • 7,220
  • 7
  • 44
  • 79
Ryan Huang
  • 3,849
  • 2
  • 13
  • 11
73

I believe ES6 could at least help with that:

[...arr].pop()

Given your array (arr) is not undefined and an iterable element (yes, even strings work!!), it should return the last element..even for the empty array and it doesn't alter it either. It creates an intermediate array though..but that should not cost much.

Your example would then look like this:

  console.log(  [...['a', 'b', 'program']].pop() );
dota2pro
  • 7,220
  • 7
  • 44
  • 79
shoesel
  • 1,198
  • 8
  • 15
  • 8
    uhh, it's quite bad, even `.slice(-1)[0]` is slighly less bad, or `var [last]=arr.slice().reverse()` is another ugly one if you need – caub Aug 08 '16 at 21:49
  • 10
    I thought this post was about ES6/ES2015, no? But I like especially your last piece. How about combining the two: ```var [last] = arr.slice(-1)``` ;) – shoesel Sep 02 '16 at 12:04
  • 14
    my favorite way is `Object.defineProperty(Array.prototype, -1, { get() {return this[this.length - 1] } }); [1,2,3][-1]` – caub Sep 02 '16 at 17:19
  • I love javascript :) – shoesel Oct 06 '16 at 09:24
  • 1
    `a = [1, 2, 3, 4, 5]; eval(\`[${('' + a).replace(/[^,]/g, '')}x] = a\`); x` yields `5` :) – shaunc Feb 10 '17 at 05:19
  • @shaunc: This only works for simple types, try adding an object to the array. – shoesel Sep 01 '17 at 09:25
  • 4
    Although this works it's mutative and removes the item from the original array. Not ideal in many scenarios. – GavKilbride Nov 27 '17 at 04:24
  • 6
    @GavKilbride it doesn't. Example is the same as `[].concat(arr).pop();` which doesn't mutate `arr`. – Dan Aug 29 '18 at 15:55
  • 1
    @caub for your first comment, instead of the reverse, I'd probably do `let [last] = arr.slice(-1);` instead of reverse if you're going to use slice and destructuring anyway – Hashbrown Jan 31 '20 at 08:11
  • 1
    @Hashbrown yea, also note that JS has `arr.at(-1)` now – caub Aug 02 '22 at 11:28
60

It is not possible in ES6/2015. The standard just doesn't provide for it.

As you can see in the spec, the FormalParameterList can either be:

  • a FunctionRestParameter
  • a FormalsList (a list of parametes)
  • a FormalsList, followed by a FunctionRestParameter

Having FunctionRestParameter followed by parameters is not provided.

Evan Davis
  • 35,493
  • 6
  • 50
  • 57
Andrey Mikhaylov - lolmaus
  • 23,107
  • 6
  • 84
  • 133
60

You can destructure the reversed array to get close to what you want.

const [a, ...rest] = ['a', 'b', 'program'].reverse();
  
document.body.innerHTML = 
    "<pre>"
    + "a: " + JSON.stringify(a) + "\n\n"
    + "rest: " + JSON.stringify(rest.reverse())
    + "</pre>";
Jeff Horton
  • 904
  • 12
  • 18
  • 3
    Smart. Better than `const last = ['bbb', 'uuu', 'iii'].slice(-1);` ? In terms of performances? – Vadorequest Jan 25 '17 at 10:00
  • 3
    the `.slice(-1)` is technically faster, but it does not give you both the last item AND the subarray in one call, which is a benefit of the `...` splat when destructuring. Performance hit of reversing twice is to be considered when used on longer arrays, but for a small script probably worth the convenience? But you could just as easily `let a = array.slice(-1), rest = array.slice(0,array.length-2)` if you still wanted the oneliner in ES5, that is ~4% faster. https://jsperf.com/last-array-splat – mix3d Nov 02 '18 at 18:22
  • this is quite clever :D – ncubica Apr 10 '22 at 02:02
  • 2
    This solution still mutates the original array , which is not ideal in real life scenarios. – Mg Gm Apr 21 '22 at 14:26
43

another approach is:

const arr = [1, 2, 3, 4, 5]
const { length, [length - 1]: last } = arr; //should be 5
console.log(last)
Den Kerny
  • 562
  • 10
  • 18
8

Not necessarily the most performant way of doing. But depending on the context a quite elegant way would be:

const myArray = ['one', 'two', 'three'];
const theOneIWant = [...myArray].pop();

console.log(theOneIWant); // 'three'
console.log(myArray.length); //3
dota2pro
  • 7,220
  • 7
  • 44
  • 79
theTaoOfJS
  • 206
  • 2
  • 3
8

Getting the last element of the array:

const [last,] = ['a', 'b', 'program'].reverse();
Chinmoy Acharjee
  • 520
  • 5
  • 16
  • 1
    I must add that JavaScript Array reverse is done in-place. So this solution might not suit every person, and/or have performance issues. I've been burned... – Shane Hsu Jun 04 '21 at 09:44
  • Basically the same as [this solution](https://stackoverflow.com/a/39676072). It still has the same problem of mutating the array. – VLAZ May 06 '22 at 11:05
6

You can try using object destructuring applied to an array to extract the length and then get last item: e.g.:

const { length, 0: first, [length - 1]: last } = ['a', 'b', 'c', 'd']

// length = 4
// first = 'a'
// last = 'd'

UPDATE

Another approach Array.prototype.at()

The at() method takes an integer value and returns the item at that index, allowing for positive and negative integers...

const last = ['a', 'b', 'c', 'd'].at(-1)
// 'd'
Serchstack
  • 91
  • 1
  • 4
5

This should work:

const [lastone] = myArray.slice(-1);
OPulido
  • 309
  • 2
  • 10
5

const arr = ['a', 'b', 'c']; // => [ 'a', 'b', 'c' ]

const {
  [arr.length - 1]: last
} = arr;

console.log(last); // => 'c'
dota2pro
  • 7,220
  • 7
  • 44
  • 79
andrhamm
  • 3,924
  • 4
  • 33
  • 46
  • 3
    Yes, I find it useful when already doing other destructuring. By itself it is not as easy to read as the classic solution. – andrhamm Nov 08 '19 at 15:27
2

Not destructing way but could help. According to javascript documentation and reverse method could be try this:

const reversed = array1.reverse();
let last_item = reversed[0]
Pamela Hdz
  • 29
  • 3
  • There is no advantage at all to use `reverse()`. It takes unnecessary time and it mutates the array which is most likely also unnecessary. With mutation `.pop()` should be the idiomatic way to get the last item. Without mutation it's `arr[arr.length - 1]` – VLAZ May 06 '22 at 11:08
2

Does es6 allow for something similar?

No, it is currently not possible, although we may get it in the future, see:

So what can we do in the meantime?

We want:

const [...rest, last] = arr;

For the time being, probably the cleanest approach is:

const arr = [1, 2, 3];

const [rest, last] = [arr.slice(0, -1), arr.at(-1)];

console.log({ rest, last });

which logs: { rest: [ 1, 2 ], last: 3 } and arr is unchanged. It does not create unnecessary copies or intermediate arrays.

Note that .at() is fairly new (as of writing):

Another approach that does not use arr.at(-1):

const arr = [1, 2, 3];

const rest = [...arr];
const last = rest.pop(); // <-- this mutates rest

console.log({ rest, last });

which logs: { rest: [ 1, 2 ], last: 3 } and arr is unchanged since we made a shallow copy first. It does not create unnecessary copies of arr or intermediate arrays either. However, I do not like the fact that rest is mutated.

If backward compatibility is important, this works even in ES3:

var arr = [1, 2, 3];

var rest = arr.slice();
var last = rest.pop(); // <-- this mutates rest
  
console.log({ rest: rest, last: last });

It is not as elegant as const [...rest, last] = arr; but the code is readable and straightforward in my opinion.

Ali
  • 56,466
  • 29
  • 168
  • 265
1

You could further destruct array´s ...rest array as Object, in order to get its length prop and build a computed prop name for the last index.

This even works for parameter destruction:

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

// Destruct from array ---------------------------
const [A_first, ...{length: l, [l - 1]: A_Last}] = a;
console.log('A: first: %o; last: %o', A_first, A_Last);     // A: first: 1; last: 4

// Destruct from fn param(s) ---------------------
const fn = ([B_first, ...{length: l, [l - 1]: B_Last}]) => {
  console.log('B: first: %o; last: %o', B_first, B_Last);   // B: first: 1; last: 4
};

fn(a);
Exodus 4D
  • 2,207
  • 2
  • 16
  • 19
0
let a = [1,2,3]
let [b] = [...a].reverse()
0

This is the most simple and understandable one I could actually come up with:

    const first = [...arr];
    const last = first.pop();
    const others = first.join(', ');
Mohi Reza
  • 19
  • 4
0

const arr = [1, 2, 3, 4, 5]; arr[arr.length - 1] = 6;

// => [1, 2, 3, 4, 6]

Aleks
  • 329
  • 3
  • 7
-1

Definitely, the question is about Destructuring for JavaScript Arrays, and we know it is not possible to have the last item of an Array by using the destructuring assignment, there is a way to do it immutable, see the following:

const arr = ['a', 'b', 'c', 'last'];

~~~
const arrLength = arr.length - 1;

const allExceptTheLast = arr.filter( (_, index) => index !== arrLength );
const [ theLastItem ] = arr.filter( (_, index) => index === arrLength );

We do not mutate the arr variable but still have all the array members except the last item as an array and have the last item separately.

AmerllicA
  • 29,059
  • 15
  • 130
  • 154
-1

Using array deconstructing: "capturing" the array, "spliced" array (arrMinusEnd), and "poped"/"sliced" element (endItem).

var [array, arrMinusEnd, endItem] = 
  ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]
  .reduce(
    (acc, cv, idx, arr) => {
      if(idx<arr.length-1) acc[1].push(cv);
      else {
        acc[0]=arr;
        acc[2]=cv;
      };
      return acc;
    },
    [null,[],[]]
  )
;

console.log("array=");
console.log(array);
console.log("arrMinusEnd=");
console.log(arrMinusEnd);
console.log("endItem=\""+endItem+"\"");
.as-console-wrapper { max-height: 100% !important; top: 0; }
iAmOren
  • 2,760
  • 2
  • 11
  • 23