What you ask for is currently not possible using the array literal and spread syntax. This is because unlike object literals, an array is not defined by specific indexes but by the sequence that becomes its members.
At most, you can use elision to leave an index empty, e.g., [1, , 3]
will produce 1, <empty>, 3
but you still have to be explicit about it, you cannot "skip over", this means that creating an array with only the first and tenth member becomes very hard [1,,,,,,,,,10]
. Having more is definitely not recommended as an array literal.
With that said, the spread syntax ...
with arrays takes values from an iterator, so [...arr]
is similar to:
let result = [];
let it = arr[Symbol.iterator]();
let next = it.next();
while (!next.done)) {
result.push(next.value);
next = it.next();
}
With this knowledge, we can leverage generator functions and make some simple generic helper generators that transform an iterable and spread their results. For example
Get everything but the last item from an iterable:
/**
*
* @generator
* @param {Iterable}
* @yields {*} every value from the iterable but the last one
*/
function* initial(iterable) {
const it = iterable[Symbol.iterator]();
let last = it.next();
let next = it.next();
while(!next.done){
yield last.value;
last = next;
next = it.next();
}
}
const arr = [1, 2, 3];
const modifiedClone = [...initial(arr), 4];
console.log(modifiedClone);
Take the fist num
values from an iterable:
/**
*
* @generator
* @param {number} num - how many items to extract
* @param {Iterable} itearable - iterable from where the results should be extracted
* @yields {*} - at most `num` amount of results from `iterable`
*/
function* take(num, iterable) {
const it = iterable[Symbol.iterator]();
let {value, done} = it.next();
for (let i = 0; i < num && !done; i++, {value, done} = it.next()) {
yield value;
}
}
const arr = [1, 2, 3];
const modifiedClone = [...take(arr.length - 1, arr), 4];
console.log(modifiedClone);
Go over an iterable and transform a value where needed:
/**
* transform an iterable of key-value pairs by their key
*
* @generator
* @param {Map} replacements - new values for each of the keys
* @param {Iterable} keyValueIterable - iterable which produces `[key, value]` pairs
* @yields {*} - the values of the iterable transformed where needed
*/
function* replaceByKey(replacements, keyValueIterable) {
const it = keyValueIterable[Symbol.iterator]();
for (let {value: pair, done} = it.next(); !done; {value: pair, done} = it.next()) {
let [key, value] = pair;
if (replacements.has(key))
yield replacements.get(key);
else
yield value;
}
}
const arr = ["a", "b", "c", "d", "e"];
const replace = new Map()
.set(1, "foo")
.set(arr.length-1, "bar");
const modifiedClone = [...replaceByKey(replace, arr.entries())];
console.log(modifiedClone);
More possibilities available depending on what is need.