Iterators and Generators - playing the prototpe
Tags: iterator, typescript, generator, infinitelist
Published: 2019-06-03 23:24:53

---
title: Iterators and Generators - playing the prototpe
published: true
description: Modifying the prototype on iterators and generators
tags: iterator,typescript,generator,infinite list
---

# Playing the Prototype

Well, may as well.

Iterators and Generators are not new concepts. Nor is modifying the prototype. But rarely are both done.

So, let's get to it!

I'm using typescript here; but the way it's written it'd be a better shim in js.

```typescript
let prot = ((function*(){}).prototype as unknown as any).__proto__ as any
```

It's ugly, but it gives us the global Generator prototype. Asserting this by other means can be annoying.

```typescript
for (let fn of gens) Object.defineProperty(prot, fn.name, {value: fn})
```

… Yeah, using generators to generate generators. As if it were that easy…

```typescript
let gens = [
  function* map<U, T>(this: IterableIterator<U>, cb: (value: U, iteration: number) => T): IterableIterator<T> {
    let iteration = 0
    for (const value of this) yield cb(value, iteration++)
  },
  function* take<T>(this: IterableIterator<T>, num: number): IterableIterator<T> {
    if (num < 1) return;
    for (const t of this) {
      yield t;
      if (--num < 1) break
    }
  },
  function* takeWhile<T>(this: IterableIterator<T>, condition: (value: T, iteration: number) => boolean): IterableIterator<T> {
    let iteration = 0
    for (const t of this) {
      if (condition(t, iteration++)) yield t
      else break
    }
  },
  function* filter<U, T = Exclude<"" | 0 | null | undefined, U>>(this: IterableIterator<U>, cb: (value: U, iteration: number) => boolean = v => !!v): IterableIterator<T> {
    let iteration = 0
    for (const value of this) if (cb(value, iteration++)) yield value as unknown as T
  },
  function fold<U, T>(this: IterableIterator<U>, init: T, cb: (acc: T, curr: U, iteration: number) => T) {
    let iteration = 0
    for (const c of this) init = cb(init, c, iteration++)
    return init
  },
  // Iterates until both are complete.
  function* zip<A, B>(this: IterableIterator<A>, other: IterableIterator<B>): IterableIterator<[A, B]> {
    try {
      let a = this.next(), b = other.next()
      while (!(a.done || b.done)) {
        yield [a.value, b.value]
        a = this.next()
        b = this.next()
      }
    } finally {
      this.return()
      other.return()
    }
  },
  function* enumerate<T>(this: IterableIterator<T>): IterableIterator<[number, T]> {
    let iteration = 0
    for (const t of this) yield [iteration++, t]
  },
  function unzip<A, B>(this: IterableIterator<[A | undefined, B | undefined]>): [A[], B[]] {
    let a = [], b = []
    for (const [aa, bb] of this) {
      if (aa !== undefined) a.push(aa);
      if (bb !== undefined) b.push(bb)
    }
    return [a, b]
  },
  function collect<T>(this: IterableIterator<T>): T[] {
    return Array.from(this)
  }
]
```

All of these are based on [`std::iter::Iterator` from rust](https://doc.rust-lang.org/std/iter/trait.Iterator.html) (13.2 MiB), and all use only the native js functions. Now you can write your infinite loop and have it go lazily … I guess.

```javascript
function* idgen(i = 0) {while(1) yield i++}
for (let value of idgen().filter(v => v % 3 == 0).map(v => v * v).take(32)) console.log(value)
0
9
36
81
144
225
324
441
576
729
900
1089
1296
1521
1764
2025
2304
2601
2916
3249
3600
3969
4356
4761
5184
5625
6084
6561
7056
7569
8100
8649
```