Monday, April 9, 2018

Extending Generator prototype to get LINQ-like experience in Javascript

Generator functions let us write iterators in a very concise way. Take a simple example:

function* range(n,m) {
    for ( var i=n; i<m; i++ ) {
        yield i;
    }
}
This lets me write
for ( var e of range(0,10) ) {
   console.log( e );
}
to get an iterator that returns numbers from 0 to 9.

An interesting question has been asked at the SO - can such generators be extended with extension methods that would work in a similar way C#'s LINQ works, where one can chain filtering, groupping or sorting operators.

My suggestion there was to extend the Generator prototype, which is technically possible and could possibly be surprising as the Generator literal doesn't resolve at the top-level. A trick here is to first resolve it manually and only then extend the prototype.

var Generator = Object.getPrototypeOf( function*() {});

Generator.prototype.filter = function*( predicate ) {
    for ( var e of this ) {
        if ( predicate(e) )
            yield e;
    }
}
This leads to following convention:
for ( var e of 
    range(0,10)
        .filter( x => x<8 )
        .filter( x => x > 2 ) ) {
    console.log(e);
}
Other operators can be added in a similar way.

No comments: