Skip to content

Instantly share code, notes, and snippets.

@falsandtru
Forked from tetsuharuohzeki/ExIterable.ts
Created March 3, 2016 18:39
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save falsandtru/affb95cebdf7b7839bb8 to your computer and use it in GitHub Desktop.
Save falsandtru/affb95cebdf7b7839bb8 to your computer and use it in GitHub Desktop.
Extentions for ECMA262 6th iterator
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,
}
}
}
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