Деструктуризация для получения последнего элемента массива в es6

116

В coffeescript это просто:

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

Допускает ли es6 нечто подобное?

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

Конечно, я могу сделать это способом es5 -

const a = b[b.length - 1]

Но, возможно, это немного подвержено ошибкам на единицу. Может ли сплат быть только последним элементом деструктуризации?

Джордж Симмс
источник
7
Почему все думают, что ES6 все меняет и есть новый синтаксис для выполнения xyz?
Феликс Клинг,
23
@FelixKling, вопрос, в частности, о поведении ...в es6, в частности о том , что его можно использовать только в последнюю очередь при деструктуризации или в списке параметров. Это потенциально противоречит здравому смыслу для кого-то, кто входит в es6 из coffeescript, и поэтому этот вопрос потенциально полезен.
Джордж Симмс,
Это означает, что [1,2,3].slice(-1)вы даже не можете деструктурировать эквивалент [1,2,3].slice(0, -1). Это обычные операции. Деструктуризация ES6 - это как-то шутка!
scriptum
@ Даже есть законная причина - обрабатывать бесконечные итерации.
Джордж Симмс
Слишком плохой отдых, так как первый параметр не работает. Было бы круто ... Вот jsperf, использующий некоторые ответы jsperf.com/destructure-last/1
Shanimal

Ответы:

46

В ES6 / 2015 это невозможно. Стандарт просто этого не предусматривает.

Как видно из спецификации , это FormalParameterListможет быть:

  • а FunctionRestParameter
  • a FormalsList(список параметров)
  • а FormalsList, за которым следуетFunctionRestParameter

После FunctionRestParameterпоследующих параметров не предусмотрен.

Андрей Михайлов - лолмаус
источник
215

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

Райан Хуанг
источник
4
Хорошее простое немутативное решение.
Джек Стим,
1
@Shanimal Depends, я получаю popверсию быстрее (OS X, Chrome 68).
Дэйв Ньютон
1
@Dave Newton Но pop () вызывает мутацию
Антонио Пантано
1
@AntonioPantano Да, он мутирует копию. Вопрос был в том, будет ли это быстрее, не дано.
Дэйв Ньютон
53

Я считаю, что ES6 может хотя бы помочь с этим:

[...arr].pop()

Учитывая, что ваш массив (arr) не является неопределенным и является повторяющимся элементом (да, даже строки работают !!), он должен возвращать последний элемент .. даже для пустого массива, и он также не меняет его. Тем не менее, он создает промежуточный массив .. но это не должно стоить дорого.

Тогда ваш пример будет выглядеть так:

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

ботинок
источник
6
эээ, это довольно плохо, даже .slice(-1)[0]немного менее плохо, или var [last]=arr.slice().reverse()еще одно уродливое, если вам нужно
caub 08
6
Я думал, этот пост про ES6 / ES2015, не так ли? Но особенно мне нравится твоя последняя работа. Как насчет того, чтобы объединить два var [last] = arr.slice(-1)
:;
13
мой любимый способObject.defineProperty(Array.prototype, -1, { get() {return this[this.length - 1] } }); [1,2,3][-1]
caub 02
3
Хотя это работает, он изменяет и удаляет элемент из исходного массива. Не идеален для многих сценариев.
GavKilbride
4
@GavKilbride это не так. Пример такой же, как тот, [].concat(arr).pop();который не изменяется arr.
Дэн
37

Вы можете деструктурировать перевернутый массив, чтобы приблизиться к желаемому.

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

KamiOfTea
источник
2
Умная. Лучше чем const last = ['bbb', 'uuu', 'iii'].slice(-1);? Что касается выступлений?
Vadorequest
1
the .slice(-1)технически быстрее, но он не дает вам одновременно последний элемент и подмассив за один вызов, что является преимуществом ...splat при деструктуризации. При использовании на более длинных массивах следует учитывать снижение производительности при двойном реверсировании, но для небольшого скрипта, вероятно, стоит удобство? Но вы могли бы так же легко, let a = array.slice(-1), rest = array.slice(0,array.length-2)если бы вам по-прежнему нужен был oneliner в ES5, это примерно на 4% быстрее. jsperf.com/last-array-splat
mix3d 02
17

другой подход:

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

Ден Керни
источник
1
@isakbob yeap, вот и вы
Ден Керни
трудно читать, или, возможно, это просто мой мозг
webjay
может быть очень полезно использовать несколько реквизитов, так что да, это только ваше, xd xd xd
Ден Керни
5

Не обязательно самый эффективный способ работы. Но в зависимости от контекста довольно элегантным способом будет:

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

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

theTaoOfJS
источник
3
Я не вижу в этом решении элегантности, но, тем не менее, @shoesel уже опубликовал его
Берги
2

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

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

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

andrhamm
источник
2
Да, я нахожу это полезным, когда уже занимаюсь деструктуризацией. Само по себе это не так легко читать, как классическое решение.
andrhamm 08
1

Это должно работать:

const [lastone] = myArray.slice(-1);
OPulido
источник
1

Вы можете попробовать этот хак:

let a = ['a', 'b', 'program'];
let [last] = a.reverse();
a.reverse();
console.log(last);
Ups Bazaar
источник
1

Не разрушающий способ, но мог бы помочь. Согласно документации javascript и обратному методу можно попробовать следующее:

const reversed = array1.reverse();
let last_item = reversed[0]
Памела Хдз
источник
0

Определенно, вопрос касается деструктуризации массивов JavaScript, и мы знаем, что невозможно получить последний элемент массива с помощью назначения деструктуризации, есть способ сделать его неизменным , см. Следующее:

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

Мы не arrизменяем переменную, но по-прежнему имеем все члены массива, кроме последнего элемента, в виде массива и последний элемент отдельно.

AmerllicA
источник
0
let a = [1,2,3]
let [b] = [...a].reverse()
Эндрю Некович
источник