15

How to make Typescript objects iterable? In Python 3 I can do

class EndlessCounter:
    def __init__(self):
        self.count = 0

    def __iter__(self):
        return self

    def __next__(self):
        self.count += 1
        return self.count

but what is the Typescript equivalent of this code?

Thanks!

3 Answers 3

21

The accepted answer will only work for a single iteration (thus it can also not support nested iterations) and the worst thing is that it will enter an endless loop when you try to iterate again.

here's a version that fixes these issues:

class Counter implements Iterable<number> {
    [Symbol.iterator]() {
        let counter = 0;
        return {
            next: () => {
                return {
                    done: counter >= 5,
                    value: counter++
                }
            }
        }
    }
}
  • whenever an iterator is requested, we return an iterator with count=0
  • each iteration will increase the closure variable counter until we are done

now multiple iterations work:

let c = new Counter();
for (let i of c) console.log(i);
for (let i of c) console.log(i);

and nested iterations also works:

let c = new Counter();
for (let i of c) {
    console.log('OUTER', i);
    for (let i of c) {
        console.log('--inner', i);
    }
}

Playground example

Generator function

An alternative is to use a generator function which results in shorter code (see Krisztián Ballas answer):

class Counter implements Iterable<number> {
    *[Symbol.iterator](): IterableIterator<number> {
        let counter = 0;
        while (counter < 5) {
            yield counter++;
        }
    }
}

Playground example

Sign up to request clarification or add additional context in comments.

Comments

15

Javascript supports Iterators and generators, typescript doesn't add much to it: typescript Iterators and Generators.

Your code can be done like this in javascript:

function* generator() {
    let counter = 0;
    while (true) {
        yield counter++;
    }
}

var iterator = generator();
console.log(iterator.next().value); // 0
console.log(iterator.next().value); // 1
console.log(iterator.next().value); // 2

Edit

You can do the same with a class:

class Counter implements Iterator<number> {
    private counter = 0;

    public next(): IteratorResult<number> {
        return {
            done: false,
            value: this.counter++
        }
    }
}

let c = new Counter();
console.log(c.next().value); // 0
console.log(c.next().value); // 1
console.log(c.next().value); // 2

2nd Edit

The first solution with the generator works well with the for/of loop:

function* generator() {
    let counter = 0;
    while (counter < 5) {
        yield counter++;
    }
}
for (let i of generator()) console.log(i);

Prints 0 to 5, however, to do that with an instance you'll need to do:

class Counter implements Iterable<number> {
    private counter = 0;

    public [Symbol.iterator]() {
        return {
            next: function() {
                return {
                    done: this.counter === 5,
                    value: this.counter++
                }
            }.bind(this)
        }
    }
}
let c = new Counter();
for (let i of c) console.log(i);

7 Comments

Thanks, but this is not direct equivalent. It's generator, not custom class with iteration.
See my modified answer
It's not the same, - you still can't do for (i of myCustomClass).
@NitzanTomer the last example in 2nd edit still doesn't work. Typescript does not have support for this. It gives an error saying c is not an array or string type. Is there a work around for this?
@sasidhar The last example requires es6 features. If you compile it with -t es6 it will work just fine.
|
7

Let's say you have a simple class that wraps a string object:

class StringWrapper {
    private readonly wrappedString: string;

    public constructor(str: string) {
        this.wrappedString = str;
    }

    *[Symbol.iterator](): IterableIterator<string> {
        for (let i = 0; i < this.wrappedString.length; ++i) {
            yield this.wrappedString[i];
        }
    }
}

The iterator is a generator function which yields one character after another from the wrapped string.

Now you can do things like this:

const wstr = new StringWrapper("abcd");

for (const char of wstr) {
    console.log(char); // will output "a", "b", "c", "d" separately
}

const anotherString == [..wstr]; // will be "abcd"

1 Comment

Really nice! I have updated my answer to have a comparison of the generator function vs. the original answer. The generator function is more readable IMHO.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.