hasNext() for ES6 Generator - javascript

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);
}

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)
}

JavaScript adding "return" method to an iterator doesn't properly close the iterator

I am learning the JavaScript ES6 iterator pattern and came across this problem:
const counter = [1, 2, 3, 4, 5];
const iter = counter[Symbol.iterator]();
iter.return = function() {
console.log("exiting early");
return { done: true };
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 4
// 5
So I added a return method definition to the iterator that I extracted from an array. Although the return method got called, it didn't actually close the iterator. By contrast, if I define the iterator return method in definition, it will work as expected:
class Counter {
[Symbol.iterator]() {
let count = 1;
return {
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
}
}
}
const myCounter = new Counter();
iter = myCounter[Symbol.iterator]();
for (let i of myCounter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of myCounter) {
console.log(i);
}
// 1
// 2
// 3
// exiting early
// ---
// 1
// 2
// 3
// 4
// 5
My question is, why did I get this unexpected behavior? I assume that if the return method didn't get called, then the iterator will not close until it reaches the very end by calling next. But adding return property will properly "call" the return method since I got the console log, but doesn't actually terminate the iterator even if I returned { done: true } in the return method.
Neither of your two return methods actually close the iterator. To achieve that, they need to record the new state of the iterator, and by that cause the next method to also return {done: true} in all subsequent calls - that's what "closed" actually means.
We can see this behaviour in action with a generator:
const iter = function*(){ yield 1; yield 2; yield 3; }();
console.log(iter.next());
console.log(iter.return());
console.log(iter.next());
Your first snippet has the problem that you've overwritten iter.return, and your method gets called (as seen from the log) but it never actually closes iter. The underlying problem is that array iterators cannot be closed, they don't normally have a return method at all. You'd have to overwrite the iter.next method as well to simulate this.
Your second snippet has the problem that it's not actually trying to iterate the iter, but it's iterating the myCounter twice which creates a new iterator object for each loop. Instead we need to use a [Symbol.iterator] method that returns the same object multiple times, easiest done by having Counter implement the iterator interface itself. We can now reproduce the unexpected behaviour:
class Counter {
count = 1;
[Symbol.iterator]() {
return this;
}
next() {
if (this.count <= 5) {
return {done: false, value: this.count++ };
} else {
return {done: true, value: undefined};
}
}
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
}
const iter = new Counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
To fix the behaviour, we would close the iterator by having the return method set the count beyond 5:
class Counter {
count = 1;
[Symbol.iterator]() {
return this;
}
next() {
if (this.count <= 5) {
return {done: false, value: this.count++ };
} else {
return {done: true, value: undefined};
}
}
return() {
this.count = 6;
// ^^^^^^^^^^^^^^^
console.log('exiting early');
return { done: true, value: undefined };
}
}
const iter = new Counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i); // not executed!
}
Your example can be simplified as
let count = 1;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (count <= 5) {
return {
done: false,
value: count++
}
} else {
return {
done: true,
value: undefined
}
}
},
return() {
console.log('exiting early');
return { done: true, value: undefined };
}
};
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}
so iter is just a normal object. You are passing it to a for..of loop twice.
You are making incorrect assumptions about how the interface for iterators works. The core issue is that there is nothing in this code that stores and tracks the fact that iter has returned done: true once, and thus should continue to do so. If that is the behavior you want, you need to do that yourself, e.g.
let count = 1;
let done = false;
const iter = {
[Symbol.iterator]() { return this; },
next() {
if (!done && count <= 5) {
return {
value: count++
}
} else {
done = true;
return { done };
}
},
return() {
done = true;
console.log('exiting early');
return { done };
}
};
A for..of loop essentially calls .next() until the return result is done: true, and calls .return in some cases. It is up to the implementation of the iterator itself to ensure that it properly enters a "closed" state.
All of this can also be simplified by using a generator function, since generator objects have that internal "closed" state included automatically as a side-effect of the function having returned, e.g.
function* counter() {
let counter = 1;
while (counter <= 5) yield counter++;
}
const iter = counter();
for (let i of iter) {
console.log(i);
if (i >= 3) {
break;
}
}
console.log('---');
for (let i of iter) {
console.log(i);
}

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);
}

Creating a Timestamped Object Array for Sampling Data in Javascript?

Goal is to push sampled data, as an object, onto an array, at a periodic interval and wait to log the new array out to the console once it is finalized.
I'm new to JS, so take it easy ;). I am likely making this more complicated than it needs to be. Thought it would be as simple as a setTimeout() in a for loop.
I have been able to generate the array two different ways, using IIFE with a setTimeout() also the setInterval() below. Not sure how to get the async await function working with an array push() method querying length. Maybe this is not a good approach?
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
function arrayGenerator(tag){
return sampleArr.push(new Sample(tag));
};
function setIntSample(callback, delay, iterations) {
var i = 0;
var intervalID = setInterval(function () {
callback(i);
if (++i === iterations) {
clearInterval(intervalID);
}
}, delay);
};
Above seems to work console.log()-ing the array as it is generated in the arrayGenerator() function. Below, no dice
function resolveAfterArrGeneration(){
return new Promise(resolve => {
arrLength = setIntSample(i => {arrayGenerator(i)}, 3000, 5)
if (arrLength === 5) {resolve();}
});
}
async function ans() {
var answer = await resolveAfterArrGeneration();
console.log(sampleArr);
}
ans();
The basic idea is to return a promise and resolve the promise when the setInterval has run enough iterations. You can do that in a single function with something like this (with extra console.logs to show the process):
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
function makeSamples(iterations, delay){
let samples = [], i = 0;
return new Promise(resolve => {
let intervalID = setInterval(function () {
console.log("pushing new sample")
samples.push(new Sample('tag: ' + i));
if (++i === iterations) {
console.log("finished resolving")
clearInterval(intervalID);
resolve(samples)
}
}, delay);
})
}
makeSamples(5, 1000).then(console.log)
I would isolate the delay part (the asynchronous) part and create a separate, generic function delay() for that. All the rest becomes simple then, using an async function and for loop:
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
class Sample {
constructor(tag, timeStamp) {
this.tag = tag;
this.timeStamp = Date.now();
}
}
async function setIntSample(callback, ms, iterations) {
const arr = [];
for (let i = 0; i < iterations; i++) {
if (i) await delay(ms); // don't delay first time
arr.push(callback(i));
}
return arr;
}
const newSample = (tag) => new Sample(tag)
console.log("wait for it....");
setIntSample(newSample, 1000, 5).then(console.log);
Another way I just got working with a generator function
function* simpleGenerator(){
var index = 0;
while (true)
yield {tag: index++, time: Date.now()}
}
var gen = simpleGenerator();
..with the corresponding push
arr.push(gen.next().value);

Distribute iterator items over subiterators

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
]);

Categories

Resources