Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to iterate over the results of a generator function

Is there a better way to iterate over the results of a generator, my for loop is ugly:

for(let job = readyJob.next(); !job.done; job = readyJob.next()){ } 

In context there is a generator function that can determine if there is a batch of work, consisting of 1..* jobs (the generator may also return no jobs in the batch). There is a continuous loop that instantiates the generator and iterates over the batch doing work on the job (logging).

Is there a more elegant solution to this iteration problem. I mean this looks like a traditional iterator from Java/C# which isn't bad. Something like an "each" would be super readable... That's my hope anyhow.

 let getReadyJob = function *(instance){
    let numJobs = 7 ; // getRandomInt(0, 10) ;
    for(let i = 0; i < numJobs; i++) {
        yield {
            jobId: '' + instance + '::' + i,
            jobReadyOn: (new Date()).valueOf()
        };
    }
}

then

while(true){
    let readyJob = getReadyJob()

    for(let job = readyJob.next(); !job.done; job = readyJob.next()){
        console.log(JSON.stringify(job.value)) ;
    }
}
like image 353
akaphenom Avatar asked Sep 17 '14 21:09

akaphenom


People also ask

How do you iterate through a generator object?

You need to call next() or loop through the generator object to access the values produced by the generator expression. When there isn't the next value in the generator object, a StopIteration exception is thrown. A for loop can be used to iterate the generator object.

Can you iterate through a generator?

Simply speaking, a generator is a function that returns an object (iterator) which we can iterate over (one value at a time).

How do generator functions return values?

A return statement in a generator, when executed, will make the generator finish (i.e. the done property of the object returned by it will be set to true ). If a value is returned, it will be set as the value property of the object returned by the generator.

Which expression can be used to delegate to another generator or iterable?

The yield* expression is used to delegate to another generator or iterable object.


2 Answers

Yes, if your environment already supports for...of:

for (var job of readyJob) {
  // ...
}

If not, have seen this a couple of times:

var next;
while (!(next = readyJob.next()).done) {
   var job = next.value;
   // ...
}
like image 100
Felix Kling Avatar answered Sep 18 '22 11:09

Felix Kling


for ... of is the elegant solution but not overall supported, however while (!(next = cursor.next()).done) { is a very cryptic, and one might ask himself, how come (var x = someFunctionCall()) is evaluated to true and another would answer welcome to javascript.

For that reason and to and give the answer another shape, it can also be done using a recursive function.

function loopThose(items) {
    const item = items.next();

    // doStuff(item);

    if (!item.done) {
        loopThose(items);
    }
}

A true benefit is when async, try catch and custom continue condition come into play:

async function printKeys(items) {
    try {
       const item = await items.next();
       // doStuff(item);
       if (isConditionFulfil(item)) {
          await this.printKeys(items);
       }
    } catch (e){

    }
}

Where a generator function yields promises that might fail:

function * list(){
  for (var i = 0; i<10; i++){
     yield fetch(buildResourceUrl(i)).then(res => res.json())
  }
}
like image 32
Tiberiu C. Avatar answered Sep 21 '22 11:09

Tiberiu C.