Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Last active December 4, 2022 14:43
Show Gist options
  • Save jonathantneal/e43d848244c9e03c801e046f9918f711 to your computer and use it in GitHub Desktop.
Save jonathantneal/e43d848244c9e03c801e046f9918f711 to your computer and use it in GitHub Desktop.
Iterator.prototype helpers polyfill — https://tc39.es/proposal-iterator-helpers
interface Iterator<T, TReturn = any, TNext = undefined> {
next(): {
done: boolean
value: T
}
/** Returns an iterator of the elements with the given mapping function applied. */
map<TT>(
/** A mapping function to call on every element of the iterator. */
callback: (value: T) => TT
): Iterator<TT, TReturn, TNext>
/** Returns an iterator of the elements with elements that meet the condition specified in the given filtering function. */
filter<TT extends T>(
/** A filtering function to call on every element of the iterator. */
callback: (value: T) => value is TT
): Iterator<TT, TReturn, TNext>
/** Returns an iterator that produces, at most, the specified number of elements, or the given number of elements produced by the underlying iterator. */
take<TT extends T>(
/** A number of elements to take. */
limit: number
): Iterator<TT, TReturn, TNext>
/** Returns an iterator that skips the given number of elements produced by the underlying iterator. */
drop<TT extends T>(
/** A number of elements to drop. */
limit: number
): Iterator<TT, TReturn, TNext>
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter. */
indexed(): Iterator<[ number, T ], TReturn, TNext>
/** Returns the accumulated result of the elements produced by the iterator. */
reduce<TT>(callback: (accumulator: TT, currentValue: T) => TT): TT
reduce<TT>(callback: (accumulator: TT, currentValue: T) => TT, initialValue: TT): TT
/** Returns an array of the iterable elements. */
toArray(): T[]
/** Calls the given callback on each element of the iterator. */
forEach(
/** A function to call on every element of the iterator. */
callback: (value: T) => void
): void
/** Returns whether the given callback function returns true for any element of the iterator. */
some(callback: (value: T) => unknown): boolean
/** Returns whether the given callback function returns true for every element of the iterator. */
every(callback: (value: T) => unknown): boolean
}
type iterator = SymbolConstructor['iterator']
interface IteratorConstructor {
new (iteratorLike: any)
from<T>(object: T): iterator extends keyof T ? ReturnType<T[iterator]> : Iterator<ReturnType<T['next']>['value']>
}
global {
var Iterator: IteratorConstructor
}
export {}
/* Iterator Helpers Polyfill v0.1.0 | CC0-1.0 */
/* 1729 bytes minified, 724 bytes gzipped. */
{
/** Prototype of the Iterator. */
let { iterator } = Symbol
let Iterator = globalThis.Iterator = function Iterator() {}
let prototypeOfIterator = Iterator.prototype = [].keys().__proto__.__proto__
let map = new WeakMap
/** Validates that a callback is a function and otherwise throws an error. */
let validateCallback = /** @type {(callback: any, name: string) => void} */ (callback, name) => {
if (typeof callback != 'function') {
throw new TypeError('Iterator.prototype.' + name + ' callback must be a function')
}
}
let validateThis = /** @type {(object: unknown) => unknown} */ (object) => {
if (!map.has(object)) throw new TypeError('Illegal invocation.')
return map.get(object)
}
let prototype = Object.getOwnPropertyDescriptors({
constructor: Iterator,
next() {
return validateThis(this).next(...arguments)
},
return() {
validateThis(this).return(...arguments)
},
throw() {
validateThis(this).throw(...arguments)
},
/** Returns an iterator of the elements with the map function applied.
@type {(
this: Iterator<unknown, unknown, unknown>,
callback: (value: unknown) => unknown
) => Iterator<unknown, unknown, unknown>}} */
map(callback) {
validateCallback(callback, 'map')
return function*(it, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) {
while ({ done, value } = it.next(), !done) {
yield callback(value)
}
}(this)
},
/** Returns an iterator of the elements with elements that meet the condition specified in the given filtering function.
@type {(
this: Iterator<unknown, unknown, unknown>,
callback: (value: unknown) => boolean
) => Iterator<unknown, unknown, unknown>} */
filter(callback) {
validateCallback(callback, 'filter')
return function*(it, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) {
while ({ done, value } = it.next(), !done) {
if (callback(value)) yield value
}
}(this)
},
/** Returns an iterator that produces, at most, the specified number of elements, or the given number of elements produced by the underlying iterator.
@type {(
this: Iterator<unknown, unknown, unknown>,
limit: number
) => Iterator<unknown, unknown, unknown>}} */
take(limit) {
limit = Number(limit) || 0
return function*(it, thisLimit = limit, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) {
while ({ done, value } = it.next(), !done) {
yield value
if (!--thisLimit) break
}
}(this)
},
/** Returns an iterator that skips the given number of elements produced by the underlying iterator.
@type {(
this: Iterator<unknown, unknown, unknown>,
limit: number
) => Iterator<unknown, unknown, unknown>} */
drop(limit) {
limit = Number(limit) || 0
return function*(it, thisLimit = limit, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) {
while ({ done, value } = it.next(), !done) {
if (--thisLimit >= 0) continue
yield value
}
}(this)
},
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter.
@type {(
this: Iterator<unknown, unknown, unknown>
) => Iterator<[ number, unknown ], unknown, unknown>} */
indexed() {
return function*(it, /** @type {number} */ index, /** @type {boolean | undefined} */ done, /** @type {unknown} */ value) {
index = -1
while ({ done, value } = it.next(), !done) {
yield [ ++index, value ]
}
}(this)
},
/** Returns an iterator where each value produced by the underlying iterator is paired with a counter.
@type {(
this: Iterator<unknown, unknown, unknown>,
callback: (accumulator: unknown, currentValue: unknown) => unknown, initialValue: unknown
) => unknown} */
reduce(callback) {
validateCallback(callback, 'reduce')
let value = 1 in arguments ? arguments[1] : this.next().value, accumulator = value, done
while ({ done, value } = this.next(), !done) {
accumulator = callback(accumulator, value)
}
return accumulator
},
/** Returns an array of the iterable elements.
@type {(
this: Iterator
) => unknown[]} */
toArray() {
return [ ...this ]
},
/** Calls the given callback on each element of the iterator.
@type {(
this: Iterator,
callback: (value: unknown) => void
) => void} */
forEach(callback) {
validateCallback(callback, 'forEach')
let done, value
while ({ done, value } = this.next(), !done) {
callback(value)
}
},
/** Returns whether the given callback function returns true for any element of the iterator.
@type {(
this: Iterator,
callback: (value: unknown) => boolean
) => boolean} */
some(callback) {
validateCallback(callback, 'every')
let every = true, done, value
while ({ done, value } = this.next(), !done) {
every = every && callback(value)
}
return Boolean(every)
},
/** Returns whether the given callback function returns true for every element of the iterator.
@type {(
this: Iterator,
callback: (value: unknown) => boolean
) => boolean} */
every(callback) {
validateCallback(callback, 'every')
let every = true, done, value
while ({ done, value } = this.next(), !done) {
every = every && callback(value)
}
return Boolean(every)
},
})
let key
for (key in prototype) {
if (key in prototypeOfIterator) delete prototype[key]
else delete prototype[key].enumerable
}
Object.defineProperties(prototypeOfIterator, prototype)
Object.defineProperties(Iterator, {
from: {
configurable: true,
value: {
from(object) {
/** @type {Iterator<unknown, unknown, unknown>} */
let returnValue
object = Object(object)
if (typeof object[iterator] == 'function') {
object = object[iterator]()
if (object instanceof Iterator) {
return object
}
}
returnValue = Object.create(prototypeOfIterator)
map.set(returnValue, object)
return returnValue
},
}.from
}
})
}
{let e,{iterator:t}=Symbol,r=globalThis.Iterator=function(){},n=r.prototype=[].keys().__proto__.__proto__,o=new WeakMap,i=(e,t)=>{if("function"!=typeof e)throw new TypeError("Iterator.prototype."+t+" callback must be a function")},u=e=>{if(!o.has(e))throw new TypeError("Illegal invocation.")
return o.get(e)},l=Object.getOwnPropertyDescriptors({constructor:r,next(){return u(this).next(...arguments)},return(){u(this).return(...arguments)},throw(){u(this).throw(...arguments)},map(e){return i(e,"map"),function*(t,r,n){for(;({done:r,value:n}=t.next()),!r;)yield e(n)}(this)},filter(e){return i(e,"filter"),function*(t,r,n){for(;({done:r,value:n}=t.next()),!r;)e(n)&&(yield n)}(this)},take(e){return e=Number(e)||0,function*(t,r=e,n,o){for(;({done:n,value:o}=t.next()),!n&&(yield o,--r););}(this)},drop(e){return e=Number(e)||0,function*(t,r=e,n,o){for(;({done:n,value:o}=t.next()),!n;)--r>=0||(yield o)}(this)},indexed(){return function*(e,t,r,n){for(t=-1;({done:r,value:n}=e.next()),!r;)yield[++t,n]}(this)},reduce(e){i(e,"reduce")
let t,r=1 in arguments?arguments[1]:this.next().value,n=r
for(;({done:t,value:r}=this.next()),!t;)n=e(n,r)
return n},toArray(){return[...this]},forEach(e){let t,r
for(i(e,"forEach");({done:t,value:r}=this.next()),!t;)e(r)},some(e){i(e,"every")
let t,r,n=!0
for(;({done:t,value:r}=this.next()),!t;)n=n&&e(r)
return Boolean(n)},every(e){i(e,"every")
let t,r,n=!0
for(;({done:t,value:r}=this.next()),!t;)n=n&&e(r)
return Boolean(n)}})
for(e in l)e in n?delete l[e]:delete l[e].enumerable
Object.defineProperties(n,l),Object.defineProperties(r,{from:{configurable:!0,value:{from(e){let i
return"function"==typeof(e=Object(e))[t]&&(e=e[t]())instanceof r?e:(i=Object.create(n),o.set(i,e),i)}}.from}})}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment