Created
February 23, 2016 18:59
-
-
Save BenoitZugmeyer/c3bbd2239b817df88240 to your computer and use it in GitHub Desktop.
Iterator utils
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
var It = (function () { | |
var STOP = {}; | |
function Iterator(next) { | |
this._next = next; | |
this._item = {}; | |
this._index = -1; | |
} | |
Iterator.prototype = { | |
constructor: Iterator, | |
[Symbol.iterator]() { return this }, | |
next() { | |
if (this._item.done) return this._item; | |
this._index += 1; | |
var result = this._next(this._index); | |
this._item.done = result === STOP; | |
if (!this._item.done) this._item.value = result; | |
return this._item; | |
} | |
}; | |
function create(next) { | |
return new Iterator(next); | |
} | |
var empty = create(function () { return STOP }); | |
function single(value) { | |
return create(function (i) { return i === 0 ? value : STOP }); | |
} | |
function getIterator(value) { | |
return value && value[Symbol.iterator](); | |
} | |
function isIterable(value) { | |
return value && typeof value[Symbol.iterator] === "function"; | |
} | |
function from(iterable) { | |
if (iterable === undefined) return empty; | |
if (iterable instanceof Iterator) return iterable; | |
if (isIterable(iterable)) return getIterator(iterable); | |
if (typeof iterable.length === "number") { | |
return create(function (index) { | |
return index < iterable.length ? iterable[index] : STOP; | |
}); | |
} | |
return single(iterable); | |
} | |
function splat(iterator) { | |
iterator = from(iterator); | |
var currentItemIterator; | |
return create(function () { | |
if (!currentItemIterator) currentItemIterator = iterator.next(); | |
while (!currentItemIterator.done) { | |
var item = currentItemIterator.value.next(); | |
if (!item.done) return item.value; | |
currentItemIterator = iterator.next(); | |
} | |
return STOP; | |
}); | |
} | |
function chain() { | |
return splat(from(arguments)); | |
} | |
function map(iterator, fn) { | |
iterator = from(iterator); | |
return create(function (index) { | |
var item = iterator.next(); | |
return item.done ? STOP : fn(item.value, index); | |
}); | |
} | |
function filter(iterator, fn) { | |
iterator = from(iterator); | |
return create(function (index) { | |
while (true) { | |
var item = iterator.next(); | |
if (item.done) return STOP; | |
if (fn(item.value, index)) return item.value; | |
} | |
}); | |
} | |
function takeWhile(iterator, fn) { | |
iterator = from(iterator); | |
return create(function (index) { | |
var item = iterator.next(); | |
return item.done || !fn(item.value, index) ? STOP : item.value; | |
}); | |
} | |
function skipWhile(iterator, fn) { | |
iterator = from(iterator); | |
var started = false; | |
return create(function () { | |
while (true) { | |
var item = iterator.next(); | |
if (item.done) return STOP; | |
if (!started) started = !fn(item.value); | |
if (started) return item.value; | |
} | |
}); | |
} | |
function skip(iterator, n) { | |
return skipWhile(iterator, function () { n--; return n >= 0 }); | |
} | |
function fold(iterator, fn, init) { | |
var previousSet = init !== undefined; | |
var previous = init; | |
for (var value of from(iterator)) { | |
if (!previousSet) { | |
previous = value; | |
previousSet = true; | |
} | |
else previous = fn(previous, value); | |
} | |
if (!previousSet) throw new Error("Fold on an empty iterator without initial value"); | |
return previous; | |
} | |
var exports = { | |
create: create, | |
empty: empty, | |
single: single, | |
isIterable: isIterable, | |
from: from, | |
splat: splat, | |
chain: chain, | |
map: map, | |
filter: filter, | |
takeWhile: takeWhile, | |
skipWhile: skipWhile, | |
skip: skip, | |
fold: fold | |
} | |
;["splat", "skip", "skipWhile", "takeWhile", "chain", "fold", "map", "filter"].forEach(function (name) { | |
var fn = exports[name]; | |
Iterator.prototype[name] = function (a, b, c) { | |
switch (arguments.length) { | |
case 0: return fn(this) | |
case 1: return fn(this, a) | |
case 2: return fn(this, a, b) | |
case 3: return fn(this, a, b, c) | |
} | |
var args = Array.from(arguments); | |
args.unshift(this); | |
return fn.apply(null, args); | |
}; | |
}) | |
return exports; | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment