I tried to filter a generator and had the expectation that this kind of general functionality must be defined anywhere in JavaScript, because it is defined for Arrays, but I can not find it. So I tried to define it. But I can not extend the built-in generators.
I have an example generator
function make_nums ()
{
let nums = {};
nums[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
return nums;
}
generating some numbers.
[...make_nums()] // => Array [ 1, 2, 3 ]
If I build an array, I can filter the array by the use of the filter
function for arrays.
[...make_nums()].filter(n => n > 1) // => Array [ 2, 3 ]
But I do not want to build an array. Instead I want to take the old generator and build a new filtering generator. For this I wrote the following function.
function filtered (generator, filter)
{
let g = {};
g[Symbol.iterator] = function* () {
for (let value of generator)
if (filter(value))
yield value;
};
return g;
}
which can be used to do what I want.
[...filtered (make_nums(), n => n > 1)] // => Array [ 2, 3 ]
But this is a very general function, which can be applied to every generator in the same way the filter
function can be applied to every Array
. So I tried to extend generators in general, but I do not understand how.
The MDN documentation for generators suggests somehow that Generator.prototype
may exist, but it does not seem to exist. When I try to define something in Generator.prototype
, I get the error
ReferenceError: Generator is not defined
How can I extend the built-in Generator
class?
With the traditional caveat that extending built-in prototypes is not necessarily the best idea, and that it's something to be done with caution, you can get the generator function prototype with
const genproto = Object.getPrototypeOf(function*(){});
With that you could add a filter()
capability:
Object.defineProperty(genproto, "filter", {
value: function*(predicate) {
for (let value of this())
if (predicate(value)) yield value;
}
});
And thus:
console.log([... function*() {
for (let i = 0; i < 10; i++) yield i;
}.filter(value => value % 2 === 0)
]);
will print [0, 2, 4, 6, 8]
.
To be clear: this answer is about extending the prototype for generator functions, not the generator objects themselves. Thus this will ensure that every generator function in the program can use that .filter()
method and any other similar extension.
It turned out that I was confused about Generator
and Iterable
. I thought I had to extend Generator
, but it is actually sufficient to extend Iterable
. And the fact that Iterable
does not seem to be defined by JavaScript either, makes it easy to define it, to avoid problems with modifications of built-in prototypes.
This actually does what I tried to achieve.
class Iterable {
constructor (generator) {
this[Symbol.iterator] = generator;
}
}
Iterable.prototype.filter = function (predicate) {
let iterable = this;
return new Iterable (function* () {
for (let value of iterable)
if (predicate (value))
yield value;
});
};
I can create Iterables
make_nums = new Iterable(function* () { yield 1; yield 2; yield 3; });
can use them
[...make_nums] // => Array [ 1, 2, 3 ]
and can filter them
[...make_nums.filter(n => n > 1)] // => Array [ 2, 3 ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With