rest end only but not rest start only?

const [...path, file] = './a/b.txt'.split('/');

current behavior: Uncaught SyntaxError: Rest element must be last element

wished behavior:

path = ['.', 'a'];
file = 'b.txt';

I understand rest operations can't be randomly in between, or both at the beginning and the end, when it comes to array (while it works perfectly fine with objects spread here or there) ... but what's the rationale to not have spread first?

example

const [...path, file] = './a/b.txt'.split('/');

// translates to
const path = './a/b.txt'.split('/');
const file = path.pop();

multiple after? sure:

const [...path, b, c] = './a/b/c.txt'.split('/');

// translates to
const path = './a/b.txt'.split('/');
const c = path.length >= 2 ? path.pop() : void 0;
const b = path.length >= 1 ? path.pop() : void 0;

// results into
path = ['.']
b = 'a';
c = 'b.txt'

thoughts?

P.S. the completition is after the amount of defined parameters after the rest in decreasing order ... so that const [...path, a] = 'a'.split('/') would result in path being an empty array, and a being "a"

I had a "smart" answer on Xwitter about this:

// "just go with this instead"
const [file, ...path] = './a/b.txt'.split('/').reverse();

the problem is that the path would be reversed too ... so the split, and the path itself, need double reverse operations ... about perf though ... and syntax, and boilerplate ... no, thanks!

(if we justified Array#reduceRight and friends, this topic is just about the same request)

last update: somebody mentioned a desire for [start, ..._, end] too, which makes my simplification for resolution more convoluted, yet this proposal doesn't strictly prevent JS to be like Python in the future:

arr = [1, 2, 3, 4, 5]
_, *rest, _, _ = arr
rest
# [2, 3]

I hope "baby steps" can work here toward a better ...rest in the near future.

Given that any use of rest syntax here causes the entire iterator to be exhausted, I agree with you that a single rest param seems like it should be permitted in any position of the array.

I'm not sure if there's other barriers to doing so, though, nor am I aware of why this restriction in ES2015 is present in the first place.

1 Like

glad we agree there's hope to improve the current state ... to complete the idea with possible ...rest in between (Python rightly throws if more than a rest exist in the pattern):

const [a, b, ..._, c, d] = 'a/b/not/relevant/c/d'.split('/');

// translates to
const _ = 'a/b/not/relevant/c/d'.split('/');

// prefixes
const a = _.length ? _.shift() : void 0;
const b = _.length ? _.shift() : void 0;

// suffixes
let after = 2;
const d =  _.length >= after-- ? _.pop() : void 0;
const c =  _.length >= after-- ? _.pop() : void 0;

// results into
a = 'a'
b = 'b'
_ = ['not', 'relevant']
c = 'c'
d = 'd'

This is a mocked logic that should always produce expected behavior

another way to think about it as interpreter/engine resolution:

const [a, b, ..._, c, d] = 'a/b/not/relevant/c/d'.split('/');

// translates to
const _ = 'a/b/not/relevant/c/d'.split('/');

// to be destructured as [a, b, _, c, d]
const result = [];

// interpreter
let before = 2;
let after = 2;

// prefix
while (before--)
  result.push(_.shift());

// intermediate
result.push(_);

// suffix
while (after--) {
  if (_.length > after)
    result.push(_.pop());
}

// test
console.log(result);

See GitHub - tc39/proposal-deiter: Double-Ended Iterator and Destructuring

3 Likes

so there is a Stage 1 proposal and I like the proposal, which uses iterators instead of my hacks too ... great, thank you!