Destruição para obter o último elemento de uma matriz em es6

116

No coffeescript, isso é direto:

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

O es6 permite algo semelhante?

> 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)                                           

Claro que posso fazer isso da maneira es5 -

const a = b[b.length - 1]

Mas talvez isso seja um pouco sujeito a erros. O splat pode ser apenas a última coisa na desestruturação?

George Simms
fonte
7
Por que todo mundo acha que o ES6 muda tudo e há alguma nova sintaxe para fazer xyz?
Felix Kling
23
@FelixKling a questão é em particular sobre o comportamento do ...em es6, particularmente que ele só pode ser usado como a última coisa durante a desestruturação ou em uma lista de parâmetros. Isso é potencialmente contra-intuitivo para alguém que entra no es6 do coffeescript e, portanto, essa pergunta é potencialmente útil.
George Simms,
Isso significa que, além disso, [1,2,3].slice(-1)você não pode nem mesmo ter uma estrutura equivalente a [1,2,3].slice(0, -1). Essas são operações comuns. A desestruturação do ES6 é de alguma forma uma piada!
scriptum
@ Mesmo que haja uma razão legítima - para lidar com iteráveis ​​infinitos.
George Simms
Péssimo descanso, pois o primeiro parâmetro não funciona. Teria sido legal ... Aqui está um jsperf usando algumas das respostas jsperf.com/destructure-last/1
Shanimal

Respostas:

46

Não é possível no ES6 / 2015. O padrão simplesmente não prevê isso.

Como você pode ver nas especificações , o FormalParameterListpode ser:

  • uma FunctionRestParameter
  • a FormalsList(uma lista de parâmetros)
  • a FormalsList, seguido por umFunctionRestParameter

Tendo FunctionRestParameterseguido os parâmetros não é fornecido.

Andrey Mikhaylov - lolmaus
fonte
215

console.log('last', [1, 3, 4, 5].slice(-1));
console.log('second_to_last', [1, 3, 4, 5].slice(-2));

Ryan Huang
fonte
4
Boa solução não mutativa simples.
Jack Steam
1
@Shanimal Depende, estou obtendo a popversão mais rápida (OS X, Chrome 68).
Dave Newton de
1
@Dave Newton Mas pop () causa mutação
Antonio Pantano
1
@AntonioPantano Sim, muda a cópia. O ponto era saber se é ou não mais rápido não é um dado adquirido.
Dave Newton
53

Acredito que ES6 poderia pelo menos ajudar com isso:

[...arr].pop()

Dado que seu array (arr) não é indefinido e é um elemento iterável (sim, até mesmo strings funcionam !!), ele deve retornar o último elemento ... mesmo para o array vazio e também não o altera. Ele cria uma matriz intermediária ... mas não deve custar muito.

Seu exemplo ficaria assim:

  console.log(  [...['a', 'b', 'program']].pop() );

calçado
fonte
6
uhh, é muito ruim, até .slice(-1)[0]é um pouco menos ruim, ou var [last]=arr.slice().reverse()é outro feio se você precisar
caub
6
Achei que esse post fosse sobre ES6 / ES2015, não? Mas gosto especialmente da sua última peça. Que tal combinar os dois var [last] = arr.slice(-1)
:;
13
meu jeito favorito éObject.defineProperty(Array.prototype, -1, { get() {return this[this.length - 1] } }); [1,2,3][-1]
caub
3
Embora isso funcione, é mutante e remove o item do array original. Não é ideal em muitos cenários.
GavKilbride
4
@GavKilbride, não. O exemplo é o mesmo [].concat(arr).pop();que não sofre mutação arr.
Dan
37

Você pode desestruturar o array invertido para chegar perto do que deseja.

const [a, ...rest] = ['a', 'b', 'program'].reverse();
  
document.body.innerHTML = 
    "<pre>"
    + "a: " + JSON.stringify(a) + "\n\n"
    + "rest: " + JSON.stringify(rest.reverse())
    + "</pre>";

KamiOfTea
fonte
2
Inteligente. Melhor que const last = ['bbb', 'uuu', 'iii'].slice(-1);? Em termos de desempenho?
Vadorequest
1
o .slice(-1)é tecnicamente mais rápido, mas não lhe dá tanto o último item eo subarray em uma chamada, que é um benefício do ...splat quando desestruturação. O acerto de desempenho de reverter duas vezes deve ser considerado quando usado em arrays mais longos, mas para um pequeno script provavelmente vale a pena a conveniência? Mas você poderia facilmente let a = array.slice(-1), rest = array.slice(0,array.length-2)se ainda quisesse o oneliner no ES5, que é ~ 4% mais rápido. jsperf.com/last-array-splat
mix3d
17

outra abordagem é:

const arr = [1, 2, 3, 4, 5]
const { length, [length - 1]: last } = arr; //should be 5
console.log(last)

Den Kerny
fonte
1
@isakbob sim, aqui está
Den Kerny
difícil de ler, ou talvez seja apenas meu cérebro
webjay
pode ser muito útil usar vários adereços, então sim, é só seu, xd xd xd
Den Kerny
5

Não necessariamente a maneira mais eficaz de fazer. Mas dependendo do contexto, uma maneira bastante elegante seria:

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

console.log(theOneIWant); // 'three'
console.log(myArray.length); //3

theTaoOfJS
fonte
3
Não vejo nenhuma elegância nesta solução, mas de qualquer maneira @shoesel já a postou
Bergi 01 de
2

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

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

console.log(last); // => 'c'

andrhamm
fonte
2
Sim, acho útil quando já estou fazendo outra desestruturação. Por si só, não é tão fácil de ler quanto a solução clássica.
andrhamm
1

Isso deve funcionar:

const [lastone] = myArray.slice(-1);
OPulido
fonte
1

Você pode tentar este hack:

let a = ['a', 'b', 'program'];
let [last] = a.reverse();
a.reverse();
console.log(last);
Ups Bazaar
fonte
0

Definitivamente, a questão é sobre Destructuring for JavaScript Arrays, e sabemos que não é possível ter o último item de um Array usando a atribuição de desestruturação, há uma maneira de fazer isso imutável , veja o seguinte:

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

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

const allExceptTheLast = arr.filter( (dep, index) => index !== arrDepth && dep );
const [ theLastItem ] = arr.filter( (dep, index) => index === arrDepth && dep );

Não alteramos a arrvariável, mas ainda temos todos os membros da matriz, exceto o último item como uma matriz e temos o último item separadamente.

AmerllicA
fonte
0
let a = [1,2,3]
let [b] = [...a].reverse()
Andrew Nekowitsch
fonte
0

Não destruindo caminho, mas poderia ajudar. De acordo com a documentação do javascript e o método reverso, pode tentar o seguinte:

const reversed = array1.reverse();
let last_item = reversed[0]
Pamela Hdz
fonte