-
-
Save falsandtru/affb95cebdf7b7839bb8 to your computer and use it in GitHub Desktop.
Extentions for ECMA262 6th iterator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ExIterable<T> implements Iterable<T> { | |
protected _source: Iterable<any>; // cheat to drop type param `R`. | |
protected _operator: Operator<any, T>; // cheat to drop type param `R`. | |
constructor(source: Iterable<T> = null) { | |
this._source = source; | |
this._operator = null; | |
} | |
lift<U>(operator: Operator<T, U>): ExIterable<U> { | |
const iterable = new ExIterable<U>(); | |
iterable._source = this; | |
iterable._operator = operator; | |
return iterable; | |
} | |
forEach(fn: (v: T, index: number) => void): void { | |
const iter: Iterator<T> = this[Symbol.iterator](); | |
let index = 0; | |
let next: IteratorResult<T> = iter.next(); | |
while (!next.done) { | |
fn(next.value, index++); | |
next = iter.next(); | |
} | |
} | |
map<U>(selector: (value: T, index: number) => U): ExIterable<U> { | |
const op = new MapOperator<T, U>(selector); | |
const lifted = this.lift<U>(op); | |
return lifted; | |
} | |
flatMap<U>(selector: (value: T, index: number) => Iterable<U>): ExIterable<U> { | |
const op = new FlatMapOperator<T, U>(selector); | |
const lifted = this.lift<U>(op); | |
return lifted; | |
} | |
filter(filter: (value: T, index: number) => boolean): ExIterable<T> { | |
const op = new FilterOperator<T>(filter); | |
const lifted = this.lift<T>(op); | |
return lifted; | |
} | |
do(action: (value: T, index: number) => void): ExIterable<T> { | |
const op = new DoOperator<T>(action); | |
const lifted = this.lift<T>(op); | |
return lifted; | |
} | |
[Symbol.iterator](): Iterator<T> { | |
const source = this._source[Symbol.iterator](); | |
if (this._operator === null) { | |
return this._source[Symbol.iterator](); | |
} | |
const iter = this._operator.call(source); | |
return iter; | |
} | |
} | |
type MapFn<T, U> = (v: T, index: number) => U; | |
class MapOperator<S, T> implements Operator<S, T> { | |
private _selector: MapFn<S, T>; | |
constructor(selector: MapFn<S, T>) { | |
this._selector = selector; | |
} | |
call(source: Iterator<S>): Iterator<T> { | |
const iter = new MapIterator<S, T>(source, this._selector); | |
return iter; | |
} | |
} | |
class MapIterator<S, T> implements Iterator<T> { | |
private _source: Iterator<S>; | |
private _selector: MapFn<S, T>; | |
private _index: number; | |
constructor(source: Iterator<S>, selector: MapFn<S, T>) { | |
this._source = source; | |
this._selector = selector; | |
this._index = 0; | |
} | |
next(): IteratorResult<T> { | |
const original: IteratorResult<S> = this._source.next(); | |
if (original.done) { | |
return { | |
value: undefined, | |
done: true, | |
}; | |
} | |
const result: T = this._selector(original.value, this._index++); | |
return { | |
done: false, | |
value: result, | |
}; | |
} | |
} | |
interface Operator<S, T> { | |
call(source: Iterator<S>): Iterator<T>; | |
} | |
type FilterFn<T> = (value: T, index: number) => boolean; | |
class FilterOperator<T> implements Operator<T, T> { | |
private _filter: FilterFn<T>; | |
constructor(filter: FilterFn<T>) { | |
this._filter = filter; | |
} | |
call(source: Iterator<T>): Iterator<T> { | |
const iter = new FilterIterator<T>(source, this._filter); | |
return iter; | |
} | |
} | |
class FilterIterator<T> implements Iterator<T> { | |
private _source: Iterator<T>; | |
private _filter: FilterFn<T>; | |
private _index: number; | |
constructor(source: Iterator<T>, filter: FilterFn<T>) { | |
this._source = source; | |
this._filter = filter; | |
this._index = 0; | |
} | |
next(): IteratorResult<T> { | |
const source = this._source; | |
const filter = this._filter; | |
let next: IteratorResult<T> = source.next(); | |
while (!next.done) { | |
const ok: boolean = filter(next.value, this._index++); | |
if (ok) { | |
return { | |
done: false, | |
value: next.value, | |
}; | |
} | |
next = source.next(); | |
} | |
return { | |
value: undefined, | |
done: true, | |
}; | |
} | |
} | |
type FlatMapFn<T, U> = (v: T, index: number) => Iterable<U>; | |
class FlatMapOperator<S, T> implements Operator<S, T> { | |
private _selector: FlatMapFn<S, T>; | |
constructor(selector: FlatMapFn<S, T>) { | |
this._selector = selector; | |
} | |
call(source: Iterator<S>): Iterator<T> { | |
const iter = new FlatMapIterator<S, T>(source, this._selector); | |
return iter; | |
} | |
} | |
class FlatMapIterator<S, T> implements Iterator<T> { | |
private _source: Iterator<S>; | |
private _inner: Iterator<T>; | |
private _selector: FlatMapFn<S, T>; | |
private _index: number; | |
constructor(source: Iterator<S>, selector: FlatMapFn<S, T>) { | |
this._source = source; | |
this._inner = null; | |
this._selector = selector; | |
this._index = 0; | |
} | |
next(): IteratorResult<T> { | |
while (true) { | |
if (this._inner === null) { | |
const outer: IteratorResult<S> = this._source.next(); | |
if (outer.done) { | |
return { | |
value: undefined, | |
done: true, | |
}; | |
} | |
const result: Iterable<T> = this._selector(outer.value, this._index++); | |
const inner = result[Symbol.iterator](); | |
if (!inner) { | |
throw new Error('selector cannot return a valid iterable.'); | |
} | |
this._inner = inner; | |
} | |
const result: IteratorResult<T> = this._inner.next(); | |
if (result.done) { | |
this._inner = null; | |
continue; | |
} | |
else { | |
return { | |
done: false, | |
value: result.value, | |
}; | |
} | |
} | |
} | |
} | |
type DoFn<T> = (value: T, index: number) => void; | |
class DoOperator<T> implements Operator<T, T> { | |
private _action: DoFn<T>; | |
constructor(action: DoFn<T>) { | |
this._action = action; | |
} | |
call(source: Iterator<T>): Iterator<T> { | |
const iter = new DoIterator<T>(source, this._action); | |
return iter; | |
} | |
} | |
class DoIterator<T> implements Iterator<T> { | |
private _source: Iterator<T>; | |
private _action: DoFn<T>; | |
private _index: number; | |
constructor(source: Iterator<T>, action: DoFn<T>) { | |
this._source = source; | |
this._action = action; | |
this._index = 0; | |
} | |
next(): IteratorResult<T> { | |
const source = this._source; | |
const next: IteratorResult<T> = source.next(); | |
if (next.done) { | |
return { | |
done: true, | |
value: undefined, | |
}; | |
} | |
const result: T = next.value; | |
const action: DoFn<T> = this._action; | |
action(result, this._index++); | |
return { | |
done: false, | |
value: result, | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const list = [ | |
[1, 2], | |
[3, 4], | |
[5, 6], | |
]; | |
(new ExIterable(list)) | |
.flatMap( (v) => v ) | |
.filter( (v) => (v % 2) === 0 ) | |
.map( (v) => v * v ) | |
.do( (v) => console.log('do:' + v) ) | |
.forEach( (v, i) => console.log(i, v) ); | |
// do:4 | |
// 0 4 | |
// do:16 | |
// 1 16 | |
// do:36 | |
// 2 36 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment