I'm looking for a way to slice an iterator, I couldn't find a proper way to do it using an existing standard method/function.
Here is what I want to do:
// Suppose I have this generator:
function* range(n){
for (let i = 0; i < n; i += 1){
yield i;
}
}
// This is obviously not desirable as we create a large array:
console.log(Array.from(range(100)).slice(2, 5));
// -> [ 2, 3, 4 ]
// What I'm looking for is an existing function that would be equivalent to this:
function* islice(iterable, start, end){
if (!end){
end = start;
start = 0;
}
dropfirsts(iterable, start);
yield* firsts(iterable, end-start);
}
function dropfirsts(iterable, n){
for (let i of range(n)){
if (iterable.next().done) return;
}
}
function* firsts(iterable, n){
for (let j of range(n)){
const item = iterable.next();
if (item.done) return;
yield item.value;
}
}
console.log(Array.from(islice(range(10), 2, 5)));
// -> [ 2, 3, 4 ]
Note that I'm not looking for a complete implementation of an islice
function, instead I'm looking for a equivalent to this inside the standard library.
As of ECMAScript 2025, you have drop
and take
, and so you can now write:
function* naturals() { // An infinite generator as example
for (let i = 0; true; i++) yield i;
}
const islice = (iterable, start=0, end=Infinity) =>
Iterator.from(iterable).take(end).drop(start);
console.log(...islice(naturals(), 2, 5)); // -> 2, 3, 4
The Iterator.from
call is not needed if iterable
is an iterator that is an instance of Iterator
(which is the case with the iterators you get from native JS functions or from generators), but this way it also works for iterables that do not implement the iterator protocol, or iterators that don't inherit from Iterator
.