Distribute iterator items over subiterators - javascript

I am playing with iterators in javascript. I found a way of chaining them so I don't have to nest them, just for readability.
It kind of works exactly like an array map chain.
What I like to do is distribute the items of the generator over a few sub iterators, catch the results and feed them to the next iterator. Such that the outcome will be this:
4
6
8
7
9
11
Given this piece of code:
"use strict";
const _ = require('lodash');
let things = chain([
gen,
addOne,
distribute([
addOne,
addTwo,
addThree
]),
addOne
]);
for(let thing of things) {
console.log(thing);
}
//////////////// DEFENITIONS ////////////////////
function* gen() {
yield* [1, 2, 3, 4, 5, 6];
}
function* addOne(iterator) {
for(let item of iterator) {
yield (item + 1)
}
}
function* addTwo(iterator) {
for(let item of iterator) {
yield (item + 2)
}
}
function* addThree(iterator) {
for(let item of iterator) {
yield (item + 3)
}
}
const distribute = _.curry(function* (iterators, iterator) {
// magic
});
function chain(iterators) {
return iterators.reduce((prevIterator, thisIterator, index) => {
if(index === 0) {
return thisIterator();
}
return thisIterator(prevIterator);
}, null);
}
Eventually i would like to add a distribution function to the distribution iterator, so it can determine which item to pass to which subiterator. For now it is just based on the order.
Question: How do I write a distribution iterator that takes a few subiterators as an argument and passes the results through to the next iterator.

It seems overly complex, but it works
const distribute = _.curry(function* (iterators, mainIterator) {
let iteratorIndex = 0;
let done = [];
for(let iterator of iterators) {
iterators[iteratorIndex] = iterator(mainIterator);
done.push(false);
iteratorIndex++;
}
while(true) {
let iteratorIndex = 0;
for(let iterator of iterators) {
let next = iterator.next();
done[iteratorIndex] = next.done;
if(!next.done) {
yield next.value;
}
iteratorIndex++;
}
if(done.every(done => done)) {
return;
}
}
});
And finally with a distribution function:
const distribute = _.curry(function* (genIteratorIndex, iterators, mainIterator) {
let iteratorIndex = 0;
let done = [];
// instantiate iterators
for(let iterator of iterators) {
iterators[iteratorIndex] = iterator(mainIterator);
done.push(false);
iteratorIndex++;
}
// Pass stuff through
while(true) {
let next = iterators[genIteratorIndex.next().value].next();
done[iteratorIndex] = next.done;
if(!next.done) {
yield next.value;
}
if(done.every(done => done === true)) {
return;
}
}
});
function* subSequent(len) {
let curr = 0;
while(true) {
if(curr === len) {
curr = 0;
}
yield curr;
curr++;
}
}
let things = chain([
gen,
addOne,
distribute(subSequent(3), [
addOne,
addTwo,
addThree
]),
addOne
]);

Related

How to return an iterator in entries, keys and values methods of a linked list?

I'm trying to implement an Array-like linked list (same methods). I have the following two classes:
class Node {
constructor(value, next_node=null, prev_node=null) {
this.value = value;
this.next_node = next_node;
this.prev_node = prev_node;
}
}
class List {
constructor() {
this.head = null;
this.tail = null;
this.length = 0;
}
}
I'm trying to implement values(), entries() and keys() similar to of Array. My code:
[Symbol.iterator]() {
let current = this.head;
return {
next() {
if (current) {
let value = current.value;
current = current.next;
return {value: value, done: false};
}
return {done: true};
}
};
}
entries() {
return this._entries();
}
keys() {
return this._keys();
}
values() {
return this._values();
}
* _entries() {
var node = this.head;
var counter = 0;
while (node) {
yield [counter,node.value];
node = node.next_node;
counter += 1;
}
}
* _keys() {
var node = this.head;
var counter = 0;
while (node) {
yield counter;
node = node.next_node;
counter += 1;
}
}
* _values() {
var node = this.head;
while (node) {
yield node.value;
node = node.next_node;
}
}
Comparing between array.entries() and list.entries() I see:
Object [Array Iterator] {}
Object [Generator] {}
I understand that there is a difference between an iterator and generator. Two questions:
Should I keep it as Generator? Why Array uses Array Iterator instead of a Generator?
If I should switch to an iterator, how it should be done for those methods? As you can see, I implemented [Symbol.iterator](), but how do I use it in entries(), keys() and values()?
I understand that there is a difference
Only in syntax, basically a generator is syntax sugar for implementing Symbol.iterator. It makes it so that you don't need to worry about handling internal state like done etc, and just worry about how to yield.
If I should switch to an iterator,
If you mean move away from using generators, as there both iterators internally anyway, then I would say no, generators are much easier to code and reason with, and implementing Symbol.iterator manually would be step backwards.
Also you can keep your code a little bit more DRY here, below is an example.
class X {
[Symbol.iterator]() { return this.values(); }
*values() {
yield 1;
yield 2;
yield 3;
}
}
const x = new X();
//Both are iterators..
console.log('x.values() is iterator? ' +(Symbol.iterator in x.values()));
console.log('x is iterator? ' + (Symbol.iterator in x));
//Both work the same.
for (const a of x.values()) console.log(a);
for (const a of x) console.log(a);
If you want to use [Symbol.iterator] for entries(), you have to return a new object with *[Symbol.iterator]
class List {
constructor(min, max) {
this.min = min
this.max = max
}
entries() {
const {
min,
max
} = this
return {
*[Symbol.iterator]() {
for (let i = min; i <= max; i += 1) {
yield i
}
}
}
}
}
const list = new List(2, 7)
console.log([...list.entries()])
for (const item of list.entries()) {
console.log(item)
}

How to sequentially combine two sync iterators in JavaScript?

Let's say we have two async iterators,
const asyncIterable1 = {
[Symbol.asyncIterator]() {
return {
i: 0,
next() {
if (this.i < 3) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
const asyncIterable2 = {
[Symbol.asyncIterator]() {
return {
i: 3,
next() {
if (this.i < 5) {
return Promise.resolve({ value: this.i++, done: false });
}
return Promise.resolve({ done: true });
}
};
}
};
Now, is there a way to combine these two iterators into one iterator that would return a sequence of 0,1,2 and then 3,4?
Yeah, I'd use yield* for that:
const combine = (a, b) => (function* () { yield* a; yield* b; })();
const iterator = combine(
(function* () { yield 1; yield 2; })(),
(function* () { yield 3; yield 4; })()
);
console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());
This works analogously for async iterators. You'll loose the return value ("the done yield") of the first iterator though. You could capture it however (the value yield* evaluates to).
For sure if you're among the people that like to reinvent wheels, you can also implement such functionality "by hand" without generator functions:
function combine(...iterators) {
let pos = 0, iterator = iterators[pos];
return {
next() {
let result = { done: true };
do {
result = iterator.next();
if(!result.done) break;
iterator = iterators[ pos++ ];
} while(iterator)
return result;
}
};
}
const iterator = combine(
(function* () { yield 1; yield 2; })(),
(function* () { yield 3; yield 4; })()
);
console.log(iterator.next(), iterator.next(), iterator.next(), iterator.next(), iterator.next());
In addition to Jonas' great answer, we could generalize a bit further and combine an arbitrary number of iterators:
let combine = function*(...iterators) {
for (let it of iterators) yield* it;
};

filter values from a javascript generator-stream

I've recently watched this interesting video on the computerphile and decided to implement the sieve-algorithm using JavaScript generators. I'm pretty confident with JavaScript in general but have never worked with generators directly before.
Here's what I have so far:
function* nextNaturalNumber(n) {
yield n;
yield* nextNaturalNumber(n + 1);
}
function* sieve(n) {
let count = 0;
let g = nextNaturalNumber(2);
while (count++ < n) {
const possiblePrime = g.next().value;
yield possiblePrime;
// here's where I'm stuck:
// how can I filter the values from the nextNaturalNumber-generator stream that are not divisible by 2?
}
}
// print the first 10 primes
for (const prime of sieve(10)) {
console.log(prime);
}
As mentioned in the code-comment, I'm stuck on how to filter the values from the generator stream that are not divisible by 2 and thus performing the "sieve"-operation. Is there a simple way to do this (as in Python with yield from sieve(i for i in s if i%n !=0)) in JavaScript?
Unfortunately, Javascript does not have that many good iterator operations. You can, however, just make a filter function that loops through the iterator and yield values that match:
function* nextNaturalNumber(n) {
// switch to iterative to avoid stack overflows
while(true) {
yield n;
n ++;
}
}
function* filterIter(iter, pred) {
for (const item of iter) {
if (pred(item)) {
yield item;
}
}
}
function* sieve(n) {
let count = 0;
let g = nextNaturalNumber(2);
while (count++ < n) {
const possiblePrime = g.next().value;
yield possiblePrime;
g = filterIter(g, v => v % possiblePrime != 0);
}
}
// print the first 10 primes
for (const prime of sieve(10)) {
console.log(prime);
}
With the following you get only odd values from your stream:
do {
val = g.next().value;
} while (!(val%2));
Here you can test it in your code:
function* nextNaturalNumber(n) {
yield n;
yield* nextNaturalNumber(n + 1);
}
function* sieve(n) {
let count = 0;
let g = nextNaturalNumber(2);
while (count++ < n) {
let val;
do {
val = g.next().value;
} while (!(val%2));
const possiblePrime=val;
yield possiblePrime;
}
}
// print the first 10 primes
for (const prime of sieve(10)) {
console.log(prime);
}
I worry that, maybe, you're trying to be more Pythonic than is really ideal for JS coding. JS encourages being explicit about your ongoing variables (rather than hiding them in the calling and filtering stack like Python does). How about this?
//
// You want to look through all the natural numbers...
//
function* nextNaturalNumber(n) {
while(true) {
yield n;
n++;
}
}
function* sieve() {
//
// Keep track of the primes you've seen previously
//
let previousPrimes = [];
for (const possiblePrime of nextNaturalNumber(2)) {
//
// And for each number, check whether it's divisible by any of those primes
//
if (!previousPrimes.find((test) => (possiblePrime % test === 0))) {
//
// If it's not, return it as a prime and add it to the
// primes you've (now) already seen
//
yield possiblePrime;
previousPrimes.push(possiblePrime);
}
}
}
let generator = sieve();
for(let i = 0; i < 10; i++) {
console.log(generator.next().value);
}

How to peek at the next value in a Javascript Iterator

Let's say I have an iterator:
function* someIterator () {
yield 1;
yield 2;
yield 3;
}
let iter = someIterator();
... that I look at the next element to be iterated:
let next = iter.next(); // {value: 1, done: false}
... and I then use the iterator in a loop:
for(let i of iterator)
console.log(i);
// 2
// 3
The loop will not include the element looked at. I wish to see the next element while not taking it out of the iteration series.
In other words, I wish to implement:
let next = peek(iter); // {value: 1, done: false}, or alternatively just 1
for(let i of iterator)
console.log(i);
// 1
// 2
// 3
... and I wan't to do it without modifying the code for the iterable function.
What I've tried is in my answer. It works (which is why I made it an answer), but I worry that it builds an object that is more complex than it has to be. And I worry that it will not work for cases where the 'done' object is something different than { value = undefined, done = true }. So any improved answers are very much welcome.
Instead of a peek function, I built a peeker function that calls next, removing the element from the iterator, but then adds it back in by creating an iterable function that first yields the captured element, then yields the remaining items in the iterable.
function peeker(iterator) {
let peeked = iterator.next();
let rebuiltIterator = function*() {
if(peeked.done)
return;
yield peeked.value;
yield* iterator;
}
return { peeked, rebuiltIterator };
}
function* someIterator () { yield 1; yield 2; yield 3; }
let iter = someIterator();
let peeked = peeker(iter);
console.log(peeked.peeked);
for(let i of peeked.rebuiltIterator())
console.log(i);
Just a bit different idea is to use wrapper that makes an iterator kind of eagier.
function peekable(iterator) {
let state = iterator.next();
const _i = (function* (initial) {
while (!state.done) {
const current = state.value;
state = iterator.next();
const arg = yield current;
}
return state.value;
})()
_i.peek = () => state;
return _i;
}
function* someIterator () { yield 1; yield 2; yield 3; }
let iter = peekable(someIterator());
let v = iter.peek();
let peeked = iter.peek();
console.log(peeked.value);
for (let i of iter) {
console.log(i);
}

hasNext() for ES6 Generator

How would I implement hasNext() method for a generator. I have tried many options like adding the generator as a return statement and yielding from the closure. Getting the first value printing it and then using the while etc, but none of them actually worked.
I know I can use for of or while like How to loop the JavaScript iterator that comes from generator? but still wondering if I can add hasNext().
function *range(start,end){
while(start < end){
yield start;
start++
}
}
let iterator = range(1,10);
// so I can do something like this.
while(iterator.hasNext()){
console.log(iterator.next().value);
}
The simple non-for…of way to loop an iterator is
for (let iterator = range(1, 10), r; !(r = iterator.next()).done; ) {
console.log(r.value);
}
If you really want to use hasNext, you can do that as well, but it's a bit weird:
const iterator = range(1, 10);
iterator.hasNext = function hasNext() {
const r = this.next();
this.current = r.value;
return !r.done;
};
while (iterator.hasNext()) {
console.log(iterator.current);
}
You can create and return an object from range having hasNext defined as Boolean by checking if start + 1 < end; recursively call a function, hasNext if generator .next().done is false and hasNext property of object at generator.next().value is set to true, else perform other task
function* range(start, end) {
let next = () => start + 1 < end;
let g = {
hasNext: next(),
currentValue: start
};
while (start < end) {
yield g;
g.currentValue = ++start;
g.hasNext = next();
}
}
let iterator = range(1, 10);
function hasNext(gen) {
let curr = gen.next();
if (!curr.done) {
if (curr.value.hasNext) {
console.log(`hasNext:${curr.value.hasNext}`
, `currentValue:${curr.value.currentValue}`);
hasNext(gen);
} else {
console.log(JSON.stringify(curr.value, null, 2));
}
}
}
hasNext(iterator);
I created this simple module. It allows you to decorate any iterator and obtain information whether there is more elements.
https://www.npmjs.com/package/iterable-has-next
Usage is as simple as that:
//...
const { extendIterator } = require('iterable-has-next')
const extendedIterator = await extendIterator(yourIterator)
while (extendedIterator.hasNext()) {
const { value } = await extendedIterator.next()
console.log(value)
}
//...
You can try it out
https://repl.it/repls/YummyWrathfulRhombus
Here's another alternative. Instead of using a generator function, create a regular function and have it return an object with custom implementations for hasNext() and next() and add a user-defined iterable for for...of loops:
function getRange(start, end) {
return {
hasNext() {
return start <= end;
},
next() {
return start <= end ? start++ : undefined;
},
*[Symbol.iterator]() {
while(start <= end) yield start++;
}
};
}
console.log('Test iteration A');
let iteratorA = getRange(1, 10);
while(iteratorA.hasNext()) {
console.log(iteratorA.next());
}
console.log('\n\nTest iteration B');
let iteratorB = getRange(20, 30);
for(let index of iteratorB) {
console.log(index);
}

Categories

Resources