2

I'm trying to write a function that can take either a list or a generator as input. For example, this function:

function x(l) {
    for (let i of l) {
        console.log(i);
    }
    for (let i of l) {
        console.log(i);
    }
}

If I run it like this:

x([1,2,3])

It will display:

1
2
3
1
2
3

Now I want to use a generator as input:

function *y() {
    yield 5
    yield 6
    yield 7
}

These don't work:

x(y())
x(y)

The output is:

5
6
7
undefined

What do I need to do so that I can make it work?

In terms of Java, the function y above is a Generator and y() is an Iterator. [1,2,3] is a list and in Java, lists are generators. But the javascript for loop expects an iterator, which means that it can't be restarted. This seems like a flaw in javascript that the for loop works on iterators and not generators.

Eyal
  • 5,728
  • 7
  • 43
  • 70

2 Answers2

4

A generator cannot be used multiple times. If you want to iterate it twice, you will need to create two generators by calling the generator function twice.

What you can do when your function expects an iterable (that is used in a for … of loop) is to create one on the fly from your generator function:

x({[Symbol.iterator]: y})

If you want to write your function x so that it can take either an iterator or a generator function, you can use something like

getIterator(val) {
    if (typeof val == "function") // see also  https://stackoverflow.com/q/16754956/1048572
        return val();
    if (typeof val[Symbol.iterator] == "function")
        return val[Symbol.iterator]();
    throw new TypeError("not iterable!")
}
function x(l) {
    for (let i of getIterator(l)) {
        console.log(i);
    }
    for (let i of getIterator(l)) { // call getIterator again
        console.log(i);
    }
}
x(y);
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    Okay! I get it now. y is a generator function, y() is an iterator, and what you've written with Symbol.iterator is an iterable, which can be reused. for loops can get an iterator or an iterable. I wanted an iterable so your one-liner works for me. Thanks! – Eyal Nov 11 '17 at 14:13
0
   let tableData = {a:1, b:2};
   function* getKey() {
      let i = 0;
      while(true){
        let obj = Object.keys(tableData)
        yield obj[i]
        i++
        if (i >= obj.length){
          i = 0
        }
      }
    }
    let i = getKey()
    i.next().value //you can use this call for ever
  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 16 '23 at 09:37