Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way I can apply filter method to a custom iterator? [duplicate]

Tags:

javascript

I'm studying how to use generator in JavaScript.

Since generator returns iterator, I thought I could use it like iterable.filter() like array.prototype.filter.

But, iterable object does not have filter, map, forEach, reduce method.

So, I made a generator version of filter, map, reduce, forEach method like the below.

My current code

'use strict';

function* range(n) {
    for (let i = 1; i <= n; i++) {
        yield i;
    }
}

function* filter(context, predicate) {

    for (let i of context) {
        if (predicate(i) === true) {
            yield i;
        }
    }

}

function isEven(value) {
    return value % 2 === 0;
}

console.log(
    ...filter(range(10), isEven)
);

This code does work, but rather than filter(range(10), isEven), I want to use it like range(10).filter(isEven).

Is there any way to accomplish what I want?

I was looking into ES6 Proxy, Reflect, but hmm... I'm not sure if these ES6 things help me.

How I want to use

range(10)
    .filter(isEven) //generator version of filter
    .map(multiplyBy5) //generator version of map
    .forEach(console.log); //generator version of forEach

Another Try - Nah... hahaha...

This code works as well, but this is like a joke...

'use strict';

function* filter(predicate) {
    for (let i of this) {
        if(predicate(i) === true) {
            yield i;
        }
    }   
}


function* map(project) {
    for (let i of this) {
        yield project(i);
    }   
}

function* range(end) {
    for (let i = 0; i <= end; i++) {
        yield i;        
    }
}

const isEven = (v) => v % 2 === 0;
const multiplyBy = (v) => (by) => v * by;

var range10_filterByEven = filter.bind(range(10), isEven);
var range10_filterByEven_multiplyBy10 = map.bind(range10_filterByEven(), multiplyBy(10));

console.log(
    ...range10_filterByEven_multiplyBy10() //0 20 40 60 80 100
);

Looks better although this is not how I want it to be.

'use strict';

function filter(predicate) {
    return function* f() {
        for (let i of this) {
                if(predicate(i) === true) {
                    yield i;
                }
            }
    }   
}


function map(project) {
    return function* m() {
        for (let i of this) {
            yield project(i);
        }  
    }
}

function range(end) {
    return function* r() {
        for (let i = 0; i <= end; i++) {
            yield i;        
        }
    }
}

const isEven = (v) => v % 2 === 0;
const multiplyBy = (v) => (by) => v * by;


function pipe() {
    return [...arguments].reduce(function(chain, current) {
        return current.bind(chain()); 
    })}

console.log(
    ...pipe(
        range(10),
        filter(isEven),
        map(multiplyBy(10))
    )()
);
like image 725
shu Avatar asked Apr 07 '18 09:04

shu


1 Answers

You could possibly just extend the generator prototype, although this looks hacky and I am not sure I would recommend this approach.

On the other hand, I believe this is the simplest approach to what you expected.

function* range(n) {
    for (let i = 1; i <= n; i++) {
        yield i;
    }
}

// "Generator" is not resolvable at the top level
var Generator = Object.getPrototypeOf(function* () {});

Generator.prototype.filter = function*(predicate) {
    for (let i of this) {
        if (predicate(i) === true) {
            yield i;
        }
    }        
}

for ( var elem of range(10).filter( x => x<5) ) {
    console.log(elem);
}

for ( var elem of range(10).filter( x => x<5 ).filter( x => x<3 ) ) {
    console.log(elem);
}

Other operators could possibly be added in a similar way.

like image 100
Wiktor Zychla Avatar answered Sep 27 '22 19:09

Wiktor Zychla