Lazy.js is a lazy evaluation library for JavaScript.
This has been done before. For examples see:
However, at least at present, Lazy.js is faster (on average) than any of those libraries. It is also more complete, with nearly all of the functionality of Underscore and Lo-Dash.
Finding your way around the code
At the heart of Lazy.js is the Sequence object. You create an initial sequence using Lazy, which can accept an array, object, or string. You can then "chain" together methods from this sequence, creating a new sequence with each call.
Here's an example:
var data = getReallyBigArray();
var statistics = Lazy(data)
.map(transform)
.filter(validate)
.reduce(aggregate);
Sequence is the foundation of other, more specific sequence types.
An ArrayLikeSequence provides indexed access to its elements.
An ObjectLikeSequence consists of key/value pairs.
A StringLikeSequence is like a string (duh): actually, it is an ArrayLikeSequence whose elements happen to be characters.
An AsyncSequence is special: it iterates over its elements
asynchronously (so calling each
generally begins an asynchronous loop and
returns immediately).
For more information
I wrote a blog post that explains a little bit more about Lazy.js, which you can read here.
You can also create an issue on GitHub if you have any issues with the library. I work through them eventually.
Lazy Lazy
Wraps an object and returns a Sequence. For null
or undefined
,
simply returns an empty sequence (see Lazy.strict for a stricter
implementation).
- For arrays, Lazy will create a sequence comprising the elements in the array (an ArrayLikeSequence).
- For objects, Lazy will create a sequence of key/value pairs (an ObjectLikeSequence).
- For strings, Lazy will create a sequence of characters (a StringLikeSequence).
Signature
function Lazy(source) { /*...*/ }
function Lazy(source) { if (isArray(source)) { return new ArrayWrapper(source); } else if (typeof source === "string") { return new StringWrapper(source); } else if (source instanceof Sequence) { return source; } if (Lazy.extensions) { var extensions = Lazy.extensions, length = extensions.length, result; while (!result && length--) { result = extensions[length](source); } if (result) { return result; } } return new ObjectWrapper(source); }
Name | Type(s) | Description |
---|---|---|
source | Array|Object|string | An array, object, or string to wrap. |
returns | Sequence | The wrapped lazy object. |
Examples
Lazy([1, 2, 4]) // instanceof Lazy.ArrayLikeSequence Lazy({ foo: "bar" }) // instanceof Lazy.ObjectLikeSequence Lazy("hello, world!") // instanceof Lazy.StringLikeSequence Lazy() // sequence: [] Lazy(null) // sequence: []
createWrapper Lazy.createWrapper
Defines a wrapper for custom StreamLikeSequences. This is useful if you want a way to handle a stream of events as a sequence, but you can't use Lazy's existing interface (i.e., you're wrapping an object from a library with its own custom events).
This method defines a factory: that is, it produces a function that can be used to wrap objects and return a Sequence. Hopefully the example will make this clear.
Signature
Lazy.createWrapper = function(initializer) { /*...*/ }
Lazy.createWrapper = function createWrapper(initializer) { var ctor = function() { this.listeners = []; }; ctor.prototype = Object.create(StreamLikeSequence.prototype); ctor.prototype.each = function(listener) { this.listeners.push(listener); }; ctor.prototype.emit = function(data) { var listeners = this.listeners; for (var len = listeners.length, i = len - 1; i >= 0; --i) { if (listeners[i](data) === false) { listeners.splice(i, 1); } } }; return function() { var sequence = new ctor(); initializer.apply(sequence, arguments); return sequence; }; }
Name | Type(s) | Description |
---|---|---|
initializer | Function | An initialization function called on objects
created by this factory. |
returns | Function | A function that creates a new StreamLikeSequence, initializes it using the specified function, and returns it. |
Examples
var factory = Lazy.createWrapper(function(eventSource) { var sequence = this; eventSource.handleEvent(function(data) { sequence.emit(data); }); }); var eventEmitter = { triggerEvent: function(data) { eventEmitter.eventHandler(data); }, handleEvent: function(handler) { eventEmitter.eventHandler = handler; }, eventHandler: function() {} }; var events = []; factory(eventEmitter).each(function(e) { events.push(e); }); eventEmitter.triggerEvent('foo'); eventEmitter.triggerEvent('bar'); events // => ['foo', 'bar']
generate Lazy.generate
Creates a GeneratedSequence using the specified generator function and (optionally) length.
Signature
Lazy.generate = function(generatorFn, length) { /*...*/ }
Lazy.generate = function generate(generatorFn, length) { return new GeneratedSequence(generatorFn, length); }
Name | Type(s) | Description |
---|---|---|
generatorFn | function(number):* | The function used to generate the sequence. This function accepts an index as a parameter and should return a value for that index in the resulting sequence. |
length | number? | The length of the sequence, for sequences with a definite length. |
returns | GeneratedSequence | The generated sequence. |
Examples
var randomNumbers = Lazy.generate(Math.random); var countingNumbers = Lazy.generate(function(i) { return i + 1; }, 5); randomNumbers // instanceof Lazy.GeneratedSequence randomNumbers.length() // => undefined countingNumbers // sequence: [1, 2, 3, 4, 5] countingNumbers.length() // => 5
range Lazy.range
Creates a sequence from a given starting value, up to a specified stopping value, incrementing by a given step. Invalid values for any of these arguments (e.g., a step of 0) result in an empty sequence.
Signature
Lazy.range = function() { /*...*/ }
Lazy.range = function range() { var start = arguments.length > 1 ? arguments[0] : 0, stop = arguments.length > 1 ? arguments[1] : arguments[0], step = arguments.length > 2 && arguments[2]; if (step === false) { step = stop > start ? 1 : -1; } if (step === 0) { return Lazy([]); } return Lazy.generate(function(i) { return start + (step * i); }) .take(Math.ceil((stop - start) / step)); }
Name | Type(s) | Description |
---|---|---|
returns | GeneratedSequence | The sequence defined by the given ranges. |
Examples
Lazy.range(3) // sequence: [0, 1, 2] Lazy.range(1, 4) // sequence: [1, 2, 3] Lazy.range(2, 10, 2) // sequence: [2, 4, 6, 8] Lazy.range(5, 1, 2) // sequence: [] Lazy.range(5, 15, -2) // sequence: [] Lazy.range(3, 10, 3) // sequence: [3, 6, 9] Lazy.range(5, 2) // sequence: [5, 4, 3] Lazy.range(7, 2, -2) // sequence: [7, 5, 3] Lazy.range(3, 5, 0) // sequence: []
repeat Lazy.repeat
Creates a sequence consisting of the given value repeated a specified number of times.
Signature
Lazy.repeat = function(value, count) { /*...*/ }
Lazy.repeat = function repeat(value, count) { return Lazy.generate(function() { return value; }, count); }
Name | Type(s) | Description |
---|---|---|
value | * | The value to repeat. |
count | number? | The number of times the value should be repeated in the sequence. If this argument is omitted, the value will repeat forever. |
returns | GeneratedSequence | The sequence containing the repeated value. |
Examples
Lazy.repeat("hi", 3) // sequence: ["hi", "hi", "hi"] Lazy.repeat("young") // instanceof Lazy.GeneratedSequence Lazy.repeat("young").length() // => undefined Lazy.repeat("young").take(3) // sequence: ["young", "young", "young"]
strict Lazy.strict
Provides a stricter version of Lazy which throws an error when
attempting to wrap null
, undefined
, or numeric or boolean values as a
sequence.
Signature
Lazy.strict = function() { /*...*/ }
Lazy.strict = function strict() { function StrictLazy(source) { if (source == null) { throw new Error("You cannot wrap null or undefined using Lazy."); } if (typeof source === "number" || typeof source === "boolean") { throw new Error("You cannot wrap primitive values using Lazy."); } return Lazy(source); }; Lazy(Lazy).each(function(property, name) { StrictLazy[name] = property; }); return StrictLazy; }
Name | Type(s) | Description |
---|---|---|
returns | Function | A stricter version of the Lazy helper function. |
Examples
var Strict = Lazy.strict(); Strict() // throws Strict(null) // throws Strict(true) // throws Strict(5) // throws Strict([1, 2, 3]) // instanceof Lazy.ArrayLikeSequence Strict({ foo: "bar" }) // instanceof Lazy.ObjectLikeSequence Strict("hello, world!") // instanceof Lazy.StringLikeSequence // Let's also ensure the static functions are still there. Strict.range(3) // sequence: [0, 1, 2] Strict.generate(Date.now) // instanceof Lazy.GeneratedSequence
Sequence Sequence
The Sequence
object provides a unified API encapsulating the notion of
zero or more consecutive elements in a collection, stream, etc.
Lazy evaluation
Generally speaking, creating a sequence should not be an expensive operation,
and should not iterate over an underlying source or trigger any side effects.
This means that chaining together methods that return sequences incurs only
the cost of creating the Sequence
objects themselves and not the cost of
iterating an underlying data source multiple times.
The following code, for example, creates 4 sequences and does nothing with
source
:
var seq = Lazy(source) // 1st sequence
.map(func) // 2nd
.filter(pred) // 3rd
.reverse(); // 4th
Lazy's convention is to hold off on iterating or otherwise doing anything
(aside from creating Sequence
objects) until you call each
:
seq.each(function(x) { console.log(x); });
Defining custom sequences
Defining your own type of sequence is relatively simple:
- Pass a method name and an object containing function overrides to
Sequence.define. If the object includes a function called
init
, this function will be called upon initialization. - The object should include at least either a
getIterator
method or aneach
method. The former supports both asynchronous and synchronous iteration, but is slightly more cumbersome to implement. The latter supports synchronous iteration and can be automatically implemented in terms of the former. You can also implement both if you want, e.g. to optimize performance. For more info, see Iterator and AsyncSequence.
As a trivial example, the following code defines a new method, sample
,
which randomly may or may not include each element from its parent.
Lazy.Sequence.define("sample", {
each: function(fn) {
return this.parent.each(function(e) {
// 50/50 chance of including this element.
if (Math.random() > 0.5) {
return fn(e);
}
});
}
});
(Of course, the above could also easily have been implemented using #filter instead of creating a custom sequence. But I did say this was a trivial example, to be fair.)
Now it will be possible to create this type of sequence from any parent sequence by calling the method name you specified. In other words, you can now do this:
Lazy(arr).sample();
Lazy(arr).map(func).sample();
Lazy(arr).map(func).filter(pred).sample();
Etc., etc.
define Sequence.define
Create a new constructor function for a type inheriting from Sequence
.
Signature
Sequence.define = function(methodName, overrides) { /*...*/ }
Sequence.define = function define(methodName, overrides) { if (!overrides || (!overrides.getIterator && !overrides.each)) { throw new Error("A custom sequence must implement *at least* getIterator or each!"); } return defineSequenceType(Sequence, methodName, overrides); }
Name | Type(s) | Description |
---|---|---|
methodName | string|Array.<string> | The name(s) of the method(s) to be
used for constructing the new sequence. The method will be attached to
the |
overrides | Object | An object containing function overrides for this
new sequence type. Must include either |
returns | Function | A constructor for a new type inheriting from |
Examples
// This sequence type logs every element to the specified logger as it // iterates over it. Lazy.Sequence.define("verbose", { init: function(logger) { this.logger = logger; }, each: function(fn) { var logger = this.logger; return this.parent.each(function(e, i) { logger(e); return fn(e, i); }); } }); Lazy([1, 2, 3]).verbose(logger).each(Lazy.noop) // calls logger 3 times
async Sequence#async
Creates a sequence, with the same elements as this one, that will be iterated
over asynchronously when calling each
.
Signature
Sequence.async = function(interval) { /*...*/ }
Sequence.async = function async(interval) { return new AsyncSequence(this, interval); }
Name | Type(s) | Description |
---|---|---|
interval | number? | The approximate period, in milliseconds, that should elapse between each element in the resulting sequence. Omitting this argument will result in the fastest possible asynchronous iteration. |
returns | AsyncSequence | The new asynchronous sequence. |
Examples
Lazy([1, 2, 3]).async(100).each(fn) // calls fn 3 times asynchronously
chunk Sequence#chunk
Breaks this sequence into chunks (arrays) of a specified length.
Signature
Sequence.chunk = function(size) { /*...*/ }
Sequence.chunk = function chunk(size) { if (size < 1) { throw new Error("You must specify a positive chunk size."); } return new ChunkedSequence(this, size); }
Name | Type(s) | Description |
---|---|---|
size | number | The size of each chunk. |
returns | Sequence | The resulting sequence of chunks. |
Examples
Lazy([]).chunk(2) // sequence: [] Lazy([1, 2, 3]).chunk(2) // sequence: [[1, 2], [3]] Lazy([1, 2, 3]).chunk(1) // sequence: [[1], [2], [3]] Lazy([1, 2, 3]).chunk(4) // sequence: [[1, 2, 3]] Lazy([1, 2, 3]).chunk(0) // throws
compact Sequence#compact
Creates a new sequence with the same elements as this one, except for all
falsy values (false
, 0
, ""
, null
, and undefined
).
Signature
Sequence.compact = function() { /*...*/ }
Sequence.compact = function compact() { return this.filter(function(e) { return !!e; }); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The new sequence. |
Examples
Lazy(["foo", null, "bar", undefined]).compact() // sequence: ["foo", "bar"]
concat Sequence#concat
Creates a new sequence with all of the elements of this one, plus those of the given array(s).
Signature
Sequence.concat = function(var_args) { /*...*/ }
Sequence.concat = function concat(var_args) { return new ConcatenatedSequence(this, arraySlice.call(arguments, 0)); }
Name | Type(s) | Description |
---|---|---|
var_args | ...* | One or more values (or arrays of values) to use for additional items after this sequence. |
returns | Sequence | The new sequence. |
Examples
var left = [1, 2, 3]; var right = [4, 5, 6]; Lazy(left).concat(right) // sequence: [1, 2, 3, 4, 5, 6] Lazy(left).concat(Lazy(right)) // sequence: [1, 2, 3, 4, 5, 6] Lazy(left).concat(right, [7, 8]) // sequence: [1, 2, 3, 4, 5, 6, 7, 8] Lazy(left).concat([4, [5, 6]]) // sequence: [1, 2, 3, 4, [5, 6]] Lazy(left).concat(Lazy([4, [5, 6]])) // sequence: [1, 2, 3, 4, [5, 6]]
consecutive Sequence#consecutive
Groups this sequence into consecutive (overlapping) segments of a specified length. If the underlying sequence has fewer elements than the specified length, then this sequence will be empty.
Signature
Sequence.consecutive = function(length) { /*...*/ }
Sequence.consecutive = function consecutive(count) { return new ConsecutiveSequence(this, count); }
Name | Type(s) | Description |
---|---|---|
length | number | The length of each consecutive segment. |
returns | Sequence | The resulting sequence of consecutive segments. |
Examples
function sum(vals) { return Lazy(vals).sum(); } var pairs = Lazy([1, 2, 3, 4]).consecutive(2); // Make sure consecutive sequences are reusable. pairs.map(sum) // => sequence: [3, 5, 7] pairs.map(sum) // => sequence: [3, 5, 7] Lazy([]).consecutive(2) // => sequence: [] Lazy([1]).consecutive(2) // => sequence: [] Lazy([1, 2]).consecutive(2) // => sequence: [[1, 2]] Lazy([1, 2, 3]).consecutive(2) // => sequence: [[1, 2], [2, 3]] Lazy([1, 2, 3]).consecutive(0) // => sequence: [[]] Lazy([1, 2, 3]).consecutive(1) // => sequence: [[1], [2], [3]]
contains Sequence#contains
Checks whether the given value is in this sequence.
Signature
Sequence.contains = function(value, equalityFn) { /*...*/ }
Sequence.contains = function contains(value, equalityFn) { return this.indexOf(value, equalityFn) !== -1; }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
equalityFn | Function? | An optional equality function, which should take two arguments and return true or false to indicate whether those values are considered equal. |
returns | boolean | True if the sequence contains the value, false if not. |
Examples
var numbers = [5, 10, 15, 20]; Lazy(numbers).contains(15) // => true Lazy(numbers).contains(13) // => false
countBy Sequence#countBy
Creates a new ObjectLikeSequence containing the unique keys of all the elements in this sequence, each paired with the number of elements in this sequence having that key.
Signature
Sequence.countBy = function(keyFn) { /*...*/ }
Sequence.countBy = function countBy(keyFn) { return new CountedSequence(this, keyFn); }
Name | Type(s) | Description |
---|---|---|
keyFn | Function|string | The function to call on the elements in this sequence to obtain a key by which to count them, or a string representing a parameter to read from all the elements in this sequence. |
returns | Sequence | The new sequence. |
Examples
function oddOrEven(x) { return x % 2 === 0 ? 'even' : 'odd'; } var numbers = [1, 2, 3, 4, 5]; Lazy(numbers).countBy(oddOrEven) // sequence: { odd: 3, even: 2 } Lazy(numbers).countBy(oddOrEven).get("odd") // => 3 Lazy(numbers).countBy(oddOrEven).get("foo") // => undefined
dropWhile
Sequence#dropWhile
aliases:
skipWhile
Creates a new sequence comprising the elements from this sequence after those that satisfy some predicate. The sequence starts with the first element that does not match the predicate.
Signature
Sequence.dropWhile = function(predicate) { /*...*/ }
Sequence.dropWhile = function dropWhile(predicate) { return new DropWhileSequence(this, predicate); }
Name | Type(s) | Description |
---|---|---|
predicate | Function | |
returns | Sequence | The new sequence |
each
Sequence#each
aliases:
forEach
Iterates over this sequence and executes a function for every element.
Signature
Sequence.each = function(fn) { /*...*/ }
Sequence.each = function each(fn) { var iterator = this.getIterator(), i = -1; while (iterator.moveNext()) { if (fn(iterator.current(), ++i) === false) { return false; } } return true; }
Name | Type(s) | Description |
---|---|---|
fn | Function | The function to call on each element in the sequence. Return false from the function to end the iteration. |
returns | boolean |
|
Examples
Lazy([1, 2, 3, 4]).each(fn) // calls fn 4 times
equals Sequence#equals
Compare this to another sequence for equality.
Signature
Sequence.equals = function(other, equalityFn) { /*...*/ }
Sequence.equals = function equals(other, equalityFn) { if (!(other instanceof Sequence)) { return false; } var it = this.getIterator(), oit = other.getIterator(), eq = equalityFn || Lazy.equality; while (it.moveNext()) { if (!oit.moveNext()) { return false; } if (!eq(it.current(), oit.current())) { return false; } } return !oit.moveNext(); }
Name | Type(s) | Description |
---|---|---|
other | Sequence | The other sequence to compare this one to. |
equalityFn | Function? | An optional equality function, which should take two arguments and return true or false to indicate whether those values are considered equal. |
returns | boolean | Whether the two sequences contain the same values in the same order. |
Examples
Lazy([1, 2]).equals(Lazy([1, 2])) // => true Lazy([1, 2]).equals(Lazy([2, 1])) // => false Lazy([1]).equals(Lazy([1, 2])) // => false Lazy([1, 2]).equals(Lazy([1])) // => false Lazy([]).equals(Lazy([])) // => true Lazy(['foo']).equals(Lazy(['foo'])) // => true Lazy(['1']).equals(Lazy([1])) // => false Lazy([false]).equals(Lazy([0])) // => false Lazy([1, 2]).equals([1, 2]) // => false Lazy([1, 2]).equals('[1, 2]') // => false
every
Sequence#every
aliases:
all
Checks whether every element in this sequence satisfies a given predicate.
Signature
Sequence.every = function(predicate) { /*...*/ }
Sequence.every = function every(predicate) { predicate = createCallback(predicate); return this.each(function(e, i) { return !!predicate(e, i); }); }
Name | Type(s) | Description |
---|---|---|
predicate | Function | A function to call on (potentially) every element in this sequence. |
returns | boolean | True if |
Examples
var numbers = [1, 2, 3, 4, 5]; var objects = [{ foo: true }, { foo: false, bar: true }]; Lazy(numbers).every(isEven) // => false Lazy(numbers).every(isPositive) // => true Lazy(objects).all('foo') // => false Lazy(objects).all('bar') // => false
filter
Sequence#filter
aliases:
select
Creates a new sequence whose values are the elements of this sequence which satisfy the specified predicate.
Signature
Sequence.filter = function(filterFn) { /*...*/ }
Sequence.filter = function filter(filterFn) { return new FilteredSequence(this, createCallback(filterFn)); }
Name | Type(s) | Description |
---|---|---|
filterFn | Function | The predicate to call on each element in this sequence, which returns true if the element should be included. |
returns | Sequence | The new sequence. |
Examples
var numbers = [1, 2, 3, 4, 5, 6]; Lazy(numbers).filter(isEven) // sequence: [2, 4, 6]
Benchmarks
function isEven(x) { return x % 2 === 0; } var smArr = Lazy.range(10).toArray(), lgArr = Lazy.range(100).toArray(); Lazy(smArr).filter(isEven).each(Lazy.noop) // lazy - 10 elements Lazy(lgArr).filter(isEven).each(Lazy.noop) // lazy - 100 elements _.each(_.filter(smArr, isEven), _.noop) // lodash - 10 elements _.each(_.filter(lgArr, isEven), _.noop) // lodash - 100 elements
Implementation | 10 elements | 100 elements |
---|---|---|
lazy | ||
lodash |
find
Sequence#find
aliases:
detect
Seaches for the first element in the sequence satisfying a given predicate.
Signature
Sequence.find = function(predicate) { /*...*/ }
Sequence.find = function find(predicate) { return this.filter(predicate).first(); }
Name | Type(s) | Description |
---|---|---|
predicate | Function | A function to call on (potentially) every element in the sequence. |
returns | * | The first element in the sequence for which |
Examples
function divisibleBy3(x) { return x % 3 === 0; } var numbers = [5, 6, 7, 8, 9, 10]; Lazy(numbers).find(divisibleBy3) // => 6 Lazy(numbers).find(isNegative) // => undefined
findWhere Sequence#findWhere
Returns the first element in this sequence with property names and values matching those of the specified object.
Signature
Sequence.findWhere = function(properties) { /*...*/ }
Sequence.findWhere = function findWhere(properties) { return this.where(properties).first(); }
Name | Type(s) | Description |
---|---|---|
properties | Object | The properties that should be found on some element in this sequence. |
returns | * | The found element, or |
Examples
var words = ["foo", "bar"]; Lazy(words).findWhere({ 0: "f" }); // => "foo" Lazy(words).findWhere({ 0: "z" }); // => undefined
first
Sequence#first
aliases:
head
take
Creates a new sequence comprising the first N elements from this sequence, OR
(if N is undefined
) simply returns the first element of this sequence.
Signature
Sequence.first = function(count) { /*...*/ }
Sequence.first = function first(count) { if (typeof count === "undefined") { return getFirst(this); } return new TakeSequence(this, count); }
Name | Type(s) | Description |
---|---|---|
count | number? | The number of elements to take from this sequence. If this value exceeds the length of the sequence, the resulting sequence will be essentially the same as this one. |
returns | * | The new sequence (or the first element from this sequence if no count was given). |
Examples
function powerOfTwo(exp) { return Math.pow(2, exp); } Lazy.generate(powerOfTwo).first() // => 1 Lazy.generate(powerOfTwo).first(5) // sequence: [1, 2, 4, 8, 16] Lazy.generate(powerOfTwo).skip(2).first() // => 4 Lazy.generate(powerOfTwo).skip(2).first(2) // sequence: [4, 8]
flatten Sequence#flatten
Creates a new sequence with every element from this sequence, and with arrays exploded so that a sequence of arrays (of arrays) becomes a flat sequence of values.
Signature
Sequence.flatten = function(shallow) { /*...*/ }
Sequence.flatten = function flatten(shallow) { return new FlattenedSequence(this, shallow); }
Name | Type(s) | Description |
---|---|---|
shallow | boolean | Option to flatten only one level deep (default is recursive). |
returns | Sequence | The new sequence. |
Examples
Lazy([1, [2, 3], [4, [5]]]).flatten() // sequence: [1, 2, 3, 4, 5] Lazy([1, [2, 3], [4, [5]]]).flatten(true) // sequence: [1, 2, 3, 4, [5]] Lazy([1, Lazy([2, 3])]).flatten() // sequence: [1, 2, 3]
get Sequence#get
Returns the element at the specified index. Note that, for sequences that are not ArrayLikeSequences, this may require partially evaluating the sequence, iterating to reach the result. (In other words for such sequences this method is not O(1).)
Signature
Sequence.get = function(i) { /*...*/ }
Sequence.get = function get(i) { var element; this.each(function(e, index) { if (index === i) { element = e; return false; } }); return element; }
Name | Type(s) | Description |
---|---|---|
i | number | The index to access. |
returns | * | The element. |
getIterator Sequence#getIterator
Creates an Iterator object with two methods, moveNext
-- returning
true or false -- and current
-- returning the current value.
This method is used when asynchronously iterating over sequences. Any type
inheriting from Sequence
must implement this method or it can't support
asynchronous iteration.
Note that this method is not intended to be used directly by application code. Rather, it is intended as a means for implementors to potentially define custom sequence types that support either synchronous or asynchronous iteration.
Signature
Sequence.getIterator = function() { /*...*/ }
Sequence.getIterator = function getIterator() { return new Iterator(this); }
Name | Type(s) | Description |
---|---|---|
returns | Iterator | An iterator object. |
Examples
var iterator = Lazy([1, 2]).getIterator(); iterator.moveNext(); // => true iterator.current(); // => 1 iterator.moveNext(); // => true iterator.current(); // => 2 iterator.moveNext(); // => false
groupBy Sequence#groupBy
Creates a new ObjectLikeSequence comprising the elements in this one, grouped together according to some key. The value associated with each key in the resulting object-like sequence is an array containing all of the elements in this sequence with that key.
Signature
Sequence.groupBy = function(keyFn, valFn) { /*...*/ }
Sequence.groupBy = function groupBy(keyFn, valFn) { return new GroupedSequence(this, keyFn, valFn); }
Name | Type(s) | Description |
---|---|---|
keyFn | Function|string | The function to call on the elements in this sequence to obtain a key by which to group them, or a string representing a parameter to read from all the elements in this sequence. |
valFn | Function|string | (Optional) The function to call on the elements in this sequence to assign to the value for each instance to appear in the group, or a string representing a parameter to read from all the elements in this sequence. |
returns | ObjectLikeSequence | The new sequence. |
Examples
function oddOrEven(x) { return x % 2 === 0 ? 'even' : 'odd'; } function square(x) { return x*x; } var numbers = [1, 2, 3, 4, 5]; Lazy(numbers).groupBy(oddOrEven) // sequence: { odd: [1, 3, 5], even: [2, 4] } Lazy(numbers).groupBy(oddOrEven).get("odd") // => [1, 3, 5] Lazy(numbers).groupBy(oddOrEven).get("foo") // => undefined Lazy(numbers).groupBy(oddOrEven, square).get("even") // => [4, 16] Lazy([ { name: 'toString' }, { name: 'toString' } ]).groupBy('name'); // => sequence: { 'toString': [ { name: 'toString' }, { name: 'toString' } ] }
indexBy Sequence#indexBy
Creates a new ObjectLikeSequence comprising the elements in this one, indexed according to some key.
Signature
Sequence.indexBy = function(keyFn, valFn) { /*...*/ }
Sequence.indexBy = function(keyFn, valFn) { return new IndexedSequence(this, keyFn, valFn); }
Name | Type(s) | Description |
---|---|---|
keyFn | Function|string | The function to call on the elements in this sequence to obtain a key by which to index them, or a string representing a property to read from all the elements in this sequence. |
valFn | Function|string | (Optional) The function to call on the elements in this sequence to assign to the value of the indexed object, or a string representing a parameter to read from all the elements in this sequence. |
returns | Sequence | The new sequence. |
Examples
var people = [ { name: 'Bob', age: 25 }, { name: 'Fred', age: 34 } ]; var bob = people[0], fred = people[1]; Lazy(people).indexBy('name') // sequence: { 'Bob': bob, 'Fred': fred } Lazy(people).indexBy('name', 'age') // sequence: { 'Bob': 25, 'Fred': 34 }
indexOf Sequence#indexOf
Performs (at worst) a linear search from the head of this sequence, returning the first index at which the specified value is found.
Signature
Sequence.indexOf = function(value, equalityFn) { /*...*/ }
Sequence.indexOf = function indexOf(value, equalityFn) { var eq = equalityFn || Lazy.equality, foundIndex = -1; this.each(function(e, i) { if (eq(e, value)) { foundIndex = i; return false; } }); return foundIndex; }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
equalityFn | Function? | An optional equality function, which should take two arguments and return true or false to indicate whether those values are considered equal. |
returns | number | The index within this sequence where the given value is located, or -1 if the sequence doesn't contain the value. |
Examples
function reciprocal(x) { return 1 / x; } Lazy(["foo", "bar", "baz"]).indexOf("bar") // => 1 Lazy([1, 2, 3]).indexOf(4) // => -1 Lazy([1, 2, 3]).map(reciprocal).indexOf(0.5) // => 1
initial Sequence#initial
Creates a new sequence comprising all but the last N elements of this sequence.
Signature
Sequence.initial = function(count) { /*...*/ }
Sequence.initial = function initial(count) { return new InitialSequence(this, count); }
Name | Type(s) | Description |
---|---|---|
count | number? | The number of items to omit from the end of the sequence (defaults to 1). |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3, 4]).initial() // sequence: [1, 2, 3] Lazy([1, 2, 3, 4]).initial(2) // sequence: [1, 2] Lazy([1, 2, 3]).filter(Lazy.identity).initial() // sequence: [1, 2]
intersection Sequence#intersection
Creates a new sequence with all the elements of this sequence that also appear among the specified arguments.
Signature
Sequence.intersection = function(var_args) { /*...*/ }
Sequence.intersection = function intersection(var_args) { if (arguments.length === 1 && isArray(arguments[0])) { return new SimpleIntersectionSequence(this, (/** @type {Array} */ var_args)); } else { return new IntersectionSequence(this, arraySlice.call(arguments, 0)); } }
Name | Type(s) | Description |
---|---|---|
var_args | ...* | The values, or array(s) of values, in which elements from this sequence must also be included to end up in the resulting sequence. |
returns | Sequence | The new sequence. |
Examples
Lazy(["foo", "bar"]).intersection([]) // sequence: [] Lazy(["foo", "bar"]).intersection(["bar", "baz"]) // sequence: ["bar"] Lazy(["a", "a"]).intersection(["a"]) // sequence: ["a"] Lazy(["a"]).intersection(["a", "a"]) // sequence: ["a"] Lazy(["a", "a"]).intersection(["a", "a"]) // sequence: ["a"] Lazy(["a", "a"]).intersection(["a"], ["a"]) // sequence: ["a"]
invoke Sequence#invoke
Creates a new sequence whose values are calculated by invoking the specified function on each element in this sequence.
Signature
Sequence.invoke = function(methodName) { /*...*/ }
Sequence.invoke = function invoke(methodName) { return this.map(function(e) { return e[methodName](); }); }
Name | Type(s) | Description |
---|---|---|
methodName | string | The name of the method to invoke for every element in this sequence. |
returns | Sequence | The new sequence. |
Examples
function Person(first, last) { this.fullName = function fullName() { return first + " " + last; }; } var people = [ new Person("Dan", "Tao"), new Person("Bob", "Smith") ]; Lazy(people).invoke("fullName") // sequence: ["Dan Tao", "Bob Smith"]
isEmpty Sequence#isEmpty
Checks whether the sequence has no elements.
Signature
Sequence.isEmpty = function() { /*...*/ }
Sequence.isEmpty = function isEmpty() { return !this.any(); }
Name | Type(s) | Description |
---|---|---|
returns | boolean | True if the sequence is empty, false if it contains at least one element. |
Examples
Lazy([]).isEmpty() // => true Lazy([1, 2, 3]).isEmpty() // => false
join
Sequence#join
aliases:
toString
Creates a string from joining together all of the elements in this sequence, separated by the given delimiter.
Signature
Sequence.join = function(delimiter) { /*...*/ }
Sequence.join = function join(delimiter) { delimiter = typeof delimiter === "undefined" ? "," : String(delimiter); var i = -1; return this.reduce(function(str, e) { if (++i > 0) { str += delimiter; } return str + e; }, ""); }
Name | Type(s) | Description |
---|---|---|
delimiter | string? | The separator to insert between every element from
this sequence in the resulting string (defaults to |
returns | string | The delimited string. |
Examples
function toParam(v, k) { return k + '=' + v; } Lazy([6, 29, 1984]).join("/") // => "6/29/1984" Lazy(["a", "b", "c"]).join() // => "a,b,c" Lazy(["a", "b", "c"]).join("") // => "abc" Lazy([1, 2, 3]).join() // => "1,2,3" Lazy([1, 2, 3]).join("") // => "123" Lazy(["", "", ""]).join(",") // => ",," Lazy([1, 2]).join(0) // => "102" Lazy(["cons", "d"]).join(true) // => "construed" Lazy({foo: 1, bar: 2}).values().join() // "1,2" Lazy({foo: 1, bar: 2}).keys().join() // "foo,bar" Lazy({foo: 1, bar: 2}).map(toParam).join('&') // 'foo=1&bar=2'
last Sequence#last
Creates a new sequence comprising the last N elements of this sequence, OR
(if N is undefined
) simply returns the last element of this sequence.
Signature
Sequence.last = function(count) { /*...*/ }
Sequence.last = function last(count) { if (typeof count === "undefined") { return this.reverse().first(); } return this.reverse().take(count).reverse(); }
Name | Type(s) | Description |
---|---|---|
count | number? | The number of items to take from the end of the sequence. |
returns | * | The new sequence (or the last element from this sequence if no count was given). |
Examples
Lazy([1, 2, 3]).last() // => 3 Lazy([1, 2, 3]).last(2) // sequence: [2, 3] Lazy([1, 2, 3]).filter(isEven).last(2) // sequence: [2]
lastIndexOf Sequence#lastIndexOf
Performs (at worst) a linear search from the tail of this sequence, returning the last index at which the specified value is found.
Signature
Sequence.lastIndexOf = function(value) { /*...*/ }
Sequence.lastIndexOf = function lastIndexOf(value, equalityFn) { var reversed = this.getIndex().reverse(), index = reversed.indexOf(value, equalityFn); if (index !== -1) { index = reversed.length() - index - 1; } return index; }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
returns | number | The last index within this sequence where the given value is located, or -1 if the sequence doesn't contain the value. |
Examples
Lazy(["a", "b", "c", "b", "a"]).lastIndexOf("b") // => 3 Lazy([1, 2, 3]).lastIndexOf(0) // => -1 Lazy([2, 2, 1, 2, 4]).filter(isEven).lastIndexOf(2) // 2
map
Sequence#map
aliases:
collect
Creates a new sequence whose values are calculated by passing this sequence's elements through some mapping function.
Signature
Sequence.map = function(mapFn) { /*...*/ }
Sequence.map = function map(mapFn) { return new MappedSequence(this, createCallback(mapFn)); }
Name | Type(s) | Description |
---|---|---|
mapFn | Function | The mapping function used to project this sequence's elements onto a new sequence. This function takes up to two arguments: the element, and the current index. |
returns | Sequence | The new sequence. |
Examples
function addIndexToValue(e, i) { return e + i; } Lazy([]).map(increment) // sequence: [] Lazy([1, 2, 3]).map(increment) // sequence: [2, 3, 4] Lazy([1, 2, 3]).map(addIndexToValue) // sequence: [1, 3, 5]
Benchmarks
function increment(x) { return x + 1; } var smArr = Lazy.range(10).toArray(), lgArr = Lazy.range(100).toArray(); Lazy(smArr).map(increment).each(Lazy.noop) // lazy - 10 elements Lazy(lgArr).map(increment).each(Lazy.noop) // lazy - 100 elements _.each(_.map(smArr, increment), _.noop) // lodash - 10 elements _.each(_.map(lgArr, increment), _.noop) // lodash - 100 elements
Implementation | 10 elements | 100 elements |
---|---|---|
lazy | ||
lodash |
max Sequence#max
Gets the maximum value in the sequence.
Signature
Sequence.max = function(valueFn) { /*...*/ }
Sequence.max = function max(valueFn) { if (typeof valueFn !== "undefined") { return this.maxBy(valueFn); } return this.reduce(function(prev, current, i) { if (typeof prev === "undefined") { return current; } return current > prev ? current : prev; }); }
Name | Type(s) | Description |
---|---|---|
valueFn | Function? | The function by which the value for comparison is calculated for each element in the sequence. |
returns | * | The element with the highest value in the sequence, or undefined if the sequence is empty. |
Examples
function reverseDigits(x) { return Number(String(x).split('').reverse().join('')); } Lazy([]).max() // => undefined Lazy([1]).max() // => 1 Lazy([1, 2]).max() // => 2 Lazy([2, 1]).max() // => 2 Lazy([6, 18, 2, 48, 29]).max() // => 48 Lazy([6, 18, 2, 48, 29]).max(reverseDigits) // => 29 Lazy(['b', 'c', 'a']).max() // => 'c'
memoize Sequence#memoize
Provides an indexed, memoized view into the sequence. This will cache the result whenever the sequence is first iterated, so that subsequent iterations will access the same element objects.
Signature
Sequence.memoize = function() { /*...*/ }
Sequence.memoize = function memoize() { return new MemoizedSequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | ArrayLikeSequence | An indexed, memoized sequence containing this sequence's elements, cached after the first iteration. |
Examples
function createObject() { return new Object(); } var plain = Lazy.generate(createObject, 10), memoized = Lazy.generate(createObject, 10).memoize(); plain.toArray()[0] === plain.toArray()[0]; // => false memoized.toArray()[0] === memoized.toArray()[0]; // => true
min Sequence#min
Gets the minimum value in the sequence.
Signature
Sequence.min = function(valueFn) { /*...*/ }
Sequence.min = function min(valueFn) { if (typeof valueFn !== "undefined") { return this.minBy(valueFn); } return this.reduce(function(prev, current, i) { if (typeof prev === "undefined") { return current; } return current < prev ? current : prev; }); }
Name | Type(s) | Description |
---|---|---|
valueFn | Function? | The function by which the value for comparison is calculated for each element in the sequence. |
returns | * | The element with the lowest value in the sequence, or undefined` if the sequence is empty. |
Examples
function negate(x) { return x * -1; } Lazy([]).min() // => undefined Lazy([1]).min() // => 1 Lazy([1, 2]).min() // => 1 Lazy([2, 1]).min() // => 1 Lazy([6, 18, 2, 49, 34]).min() // => 2 Lazy([6, 18, 2, 49, 34]).min(negate) // => 49 Lazy(['b', 'a', 'c']).min() // => 'a'
none Sequence#none
Checks whether NO elements in this sequence satisfy the given predicate (the opposite of Sequence#all, basically).
Signature
Sequence.none = function(predicate) { /*...*/ }
Sequence.none = function none(predicate) { return !this.any(predicate); }
Name | Type(s) | Description |
---|---|---|
predicate | Function? | A function to call on (potentially) every element in this sequence. |
returns | boolean | True if |
Examples
var numbers = [1, 2, 3, 4, 5]; Lazy(numbers).none() // => false Lazy(numbers).none(isEven) // => false Lazy(numbers).none(isNegative) // => true Lazy([]).none(isEven) // => true Lazy([]).none(isNegative) // => true Lazy([]).none() // => true
ofType Sequence#ofType
Creates a new sequence whose values have the specified type, as determined
by the typeof
operator.
Signature
Sequence.ofType = function(type) { /*...*/ }
Sequence.ofType = function ofType(type) { return this.filter(function(e) { return typeof e === type; }); }
Name | Type(s) | Description |
---|---|---|
type | string | The type of elements to include from the underlying
sequence, i.e. where |
returns | Sequence | The new sequence, comprising elements of the specified type. |
Examples
Lazy([1, 2, 'foo', 'bar']).ofType('number') // sequence: [1, 2] Lazy([1, 2, 'foo', 'bar']).ofType('string') // sequence: ['foo', 'bar'] Lazy([1, 2, 'foo', 'bar']).ofType('boolean') // sequence: []
pluck Sequence#pluck
Creates a new sequence whose values are calculated by accessing the specified property from each element in this sequence.
Signature
Sequence.pluck = function(propertyName) { /*...*/ }
Sequence.pluck = function pluck(property) { return this.map(property); }
Name | Type(s) | Description |
---|---|---|
propertyName | string | The name of the property to access for every element in this sequence. |
returns | Sequence | The new sequence. |
Examples
var people = [ { first: "Dan", last: "Tao" }, { first: "Bob", last: "Smith" } ]; Lazy(people).pluck("last") // sequence: ["Tao", "Smith"]
reduce
Sequence#reduce
aliases:
inject
foldl
Aggregates a sequence into a single value according to some accumulator function.
For an asynchronous sequence, instead of immediately returning a result
(which it can't, obviously), this method returns an AsyncHandle
whose onComplete
method can be called to supply a callback to handle the
final result once iteration has completed.
Signature
Sequence.reduce = function(aggregator, memo) { /*...*/ }
Sequence.reduce = function reduce(aggregator, memo) { if (arguments.length < 2) { return this.tail().reduce(aggregator, this.head()); } var eachResult = this.each(function(e, i) { memo = aggregator(memo, e, i); }); // TODO: Think of a way more efficient solution to this problem. if (eachResult instanceof AsyncHandle) { return eachResult.then(function() { return memo; }); } return memo; }
Name | Type(s) | Description |
---|---|---|
aggregator | Function | The function through which to pass every element in the sequence. For every element, the function will be passed the total aggregated result thus far and the element itself, and should return a new aggregated result. |
memo | *? | The starting value to use for the aggregated result (defaults to the first element in the sequence). |
returns | * | The result of the aggregation, or, for asynchronous sequences,
an AsyncHandle whose |
Examples
function multiply(x, y) { return x * y; } var numbers = [1, 2, 3, 4]; Lazy(numbers).reduce(multiply) // => 24 Lazy(numbers).reduce(multiply, 5) // => 120
reduceRight
Sequence#reduceRight
aliases:
foldr
Aggregates a sequence, from the tail, into a single value according to some accumulator function.
Signature
Sequence.reduceRight = function(aggregator, memo) { /*...*/ }
Sequence.reduceRight = function reduceRight(aggregator, memo) { if (arguments.length < 2) { return this.initial(1).reduceRight(aggregator, this.last()); } // This bothers me... but frankly, calling reverse().reduce() is potentially // going to eagerly evaluate the sequence anyway; so it's really not an issue. var indexed = this.getIndex(), i = indexed.length() - 1; return indexed.reverse().reduce(function(m, e) { return aggregator(m, e, i--); }, memo); }
Name | Type(s) | Description |
---|---|---|
aggregator | Function | The function through which to pass every element in the sequence. For every element, the function will be passed the total aggregated result thus far and the element itself, and should return a new aggregated result. |
memo | * | The starting value to use for the aggregated result. |
returns | * | The result of the aggregation. |
Examples
function append(s1, s2) { return s1 + s2; } function isVowel(str) { return "aeiou".indexOf(str) !== -1; } Lazy("abcde").reduceRight(append) // => "edcba" Lazy("abcde").filter(isVowel).reduceRight(append) // => "ea"
reject Sequence#reject
Creates a new sequence whose values exclude the elements of this sequence identified by the specified predicate.
Signature
Sequence.reject = function(rejectFn) { /*...*/ }
Sequence.reject = function reject(rejectFn) { rejectFn = createCallback(rejectFn); return this.filter(function(e) { return !rejectFn(e); }); }
Name | Type(s) | Description |
---|---|---|
rejectFn | Function | The predicate to call on each element in this sequence, which returns true if the element should be omitted. |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3, 4, 5]).reject(isEven) // sequence: [1, 3, 5] Lazy([{ foo: 1 }, { bar: 2 }]).reject('foo') // sequence: [{ bar: 2 }] Lazy([{ foo: 1 }, { foo: 2 }]).reject({ foo: 2 }) // sequence: [{ foo: 1 }]
rest
Sequence#rest
aliases:
skip
tail
rest
Creates a new sequence comprising all but the first N elements of this sequence.
Signature
Sequence.rest = function(count) { /*...*/ }
Sequence.rest = function rest(count) { return new DropSequence(this, count); }
Name | Type(s) | Description |
---|---|---|
count | number? | The number of items to omit from the beginning of the sequence (defaults to 1). |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3, 4]).rest() // sequence: [2, 3, 4] Lazy([1, 2, 3, 4]).rest(0) // sequence: [1, 2, 3, 4] Lazy([1, 2, 3, 4]).rest(2) // sequence: [3, 4] Lazy([1, 2, 3, 4]).rest(5) // sequence: []
reverse Sequence#reverse
Creates a new sequence with the same elements as this one, but to be iterated in the opposite order.
Note that in some (but not all) cases, the only way to create such a sequence
may require iterating the entire underlying source when each
is called.
Signature
Sequence.reverse = function() { /*...*/ }
Sequence.reverse = function reverse() { return new ReversedSequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3]).reverse() // sequence: [3, 2, 1] Lazy([]).reverse() // sequence: []
shuffle Sequence#shuffle
Creates a new sequence with the same elements as this one, in a randomized order.
Signature
Sequence.shuffle = function() { /*...*/ }
Sequence.shuffle = function shuffle() { return new ShuffledSequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3, 4, 5]).shuffle().value() // =~ [1, 2, 3, 4, 5] Lazy([]).shuffle().value() // => [] Lazy([1]).shuffle().each(Lazy.noop) // => true Lazy([]).shuffle().each(Lazy.noop) // => true
size Sequence#size
Gets the number of elements in the sequence. In some cases, this may require eagerly evaluating the sequence.
Signature
Sequence.size = function() { /*...*/ }
Sequence.size = function size() { return this.getIndex().length(); }
Name | Type(s) | Description |
---|---|---|
returns | number | The number of elements in the sequence. |
Examples
Lazy([1, 2, 3]).size(); // => 3 Lazy([1, 2]).map(Lazy.identity).size(); // => 2 Lazy([1, 2, 3]).reject(isEven).size(); // => 2 Lazy([1, 2, 3]).take(1).size(); // => 1 Lazy({ foo: 1, bar: 2 }).size(); // => 2 Lazy('hello').size(); // => 5
some
Sequence#some
aliases:
any
Checks whether at least one element in this sequence satisfies a given predicate (or, if no predicate is specified, whether the sequence contains at least one element).
Signature
Sequence.some = function(predicate) { /*...*/ }
Sequence.some = function some(predicate) { predicate = createCallback(predicate, true); var success = false; this.each(function(e) { if (predicate(e)) { success = true; return false; } }); return success; }
Name | Type(s) | Description |
---|---|---|
predicate | Function? | A function to call on (potentially) every element in this sequence. |
returns | boolean | True if |
Examples
var numbers = [1, 2, 3, 4, 5]; Lazy(numbers).some() // => true Lazy(numbers).some(isEven) // => true Lazy(numbers).some(isNegative) // => false Lazy([]).some() // => false
sort Sequence#sort
Creates a new sequence with the same elements as this one, but ordered using the specified comparison function.
This has essentially the same behavior as calling
Array#sort
,
but obviously instead of modifying the collection it returns a new
Sequence object.
Signature
Sequence.sort = function(sortFn, descending) { /*...*/ }
Sequence.sort = function sort(sortFn, descending) { sortFn || (sortFn = compare); if (descending) { sortFn = reverseArguments(sortFn); } return new SortedSequence(this, sortFn); }
Name | Type(s) | Description |
---|---|---|
sortFn | Function? | The function used to compare elements in the sequence. The function will be passed two elements and should return:
|
descending | boolean | Whether or not the resulting sequence should be
in descending order (defaults to |
returns | Sequence | The new sequence. |
Examples
Lazy([5, 10, 1]).sort() // sequence: [1, 5, 10] Lazy(['foo', 'bar']).sort() // sequence: ['bar', 'foo'] Lazy(['b', 'c', 'a']).sort(null, true) // sequence: ['c', 'b', 'a'] Lazy([5, 10, 1]).sort(null, true) // sequence: [10, 5, 1] // Sorting w/ custom comparison function Lazy(['a', 'ab', 'aa', 'ba', 'b', 'abc']).sort(function compare(x, y) { if (x.length && (x.length !== y.length)) { return compare(x.length, y.length); } if (x === y) { return 0; } return x > y ? 1 : -1; }); // => sequence: ['a', 'b', 'aa', 'ab', 'ba', 'abc']
sortBy Sequence#sortBy
Creates a new sequence with the same elements as this one, but ordered by the results of the given function.
You can pass:
- a string, to sort by the named property
- a function, to sort by the result of calling the function on each element
Signature
Sequence.sortBy = function(sortFn, descending) { /*...*/ }
Sequence.sortBy = function sortBy(sortFn, descending) { sortFn = createComparator(sortFn); if (descending) { sortFn = reverseArguments(sortFn); } return new SortedSequence(this, sortFn); }
Name | Type(s) | Description |
---|---|---|
sortFn | Function | The function to call on the elements in this sequence, in order to sort them. |
descending | boolean | Whether or not the resulting sequence should be
in descending order (defaults to |
returns | Sequence | The new sequence. |
Examples
function population(country) { return country.pop; } function area(country) { return country.sqkm; } var countries = [ { name: "USA", pop: 320000000, sqkm: 9600000 }, { name: "Brazil", pop: 194000000, sqkm: 8500000 }, { name: "Nigeria", pop: 174000000, sqkm: 924000 }, { name: "China", pop: 1350000000, sqkm: 9700000 }, { name: "Russia", pop: 143000000, sqkm: 17000000 }, { name: "Australia", pop: 23000000, sqkm: 7700000 } ]; Lazy(countries).sortBy(population).last(3).pluck('name') // sequence: ["Brazil", "USA", "China"] Lazy(countries).sortBy(area).last(3).pluck('name') // sequence: ["USA", "China", "Russia"] Lazy(countries).sortBy(area, true).first(3).pluck('name') // sequence: ["Russia", "China", "USA"]
Benchmarks
var randoms = Lazy.generate(Math.random).take(100).toArray(); Lazy(randoms).sortBy(Lazy.identity).each(Lazy.noop) // lazy _.each(_.sortBy(randoms, Lazy.identity), _.noop) // lodash
Implementation | Ops/second |
---|---|
lazy | |
lodash |
sortedIndex Sequence#sortedIndex
Performs a binary search of this sequence, returning the lowest index where the given value is either found, or where it belongs (if it is not already in the sequence).
This method assumes the sequence is in sorted order and will fail otherwise.
Signature
Sequence.sortedIndex = function(value) { /*...*/ }
Sequence.sortedIndex = function sortedIndex(value) { var indexed = this.getIndex(), lower = 0, upper = indexed.length(), i; while (lower < upper) { i = (lower + upper) >>> 1; if (compare(indexed.get(i), value) === -1) { lower = i + 1; } else { upper = i; } } return lower; }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
returns | number | An index within this sequence where the given value is located, or where it belongs in sorted order. |
Examples
Lazy([1, 3, 6, 9]).sortedIndex(3) // => 1 Lazy([1, 3, 6, 9]).sortedIndex(7) // => 3 Lazy([5, 10, 15, 20]).filter(isEven).sortedIndex(10) // => 0 Lazy([5, 10, 15, 20]).filter(isEven).sortedIndex(12) // => 1
sum Sequence#sum
Gets the sum of the numeric values in the sequence.
Signature
Sequence.sum = function(valueFn) { /*...*/ }
Sequence.sum = function sum(valueFn) { if (typeof valueFn !== "undefined") { return this.sumBy(valueFn); } return this.reduce(function(x, y) { return x + y; }, 0); }
Name | Type(s) | Description |
---|---|---|
valueFn | Function? | The function used to select the numeric values that will be summed up. |
returns | * | The sum. |
Examples
Lazy([]).sum() // => 0 Lazy([1, 2, 3, 4]).sum() // => 10 Lazy([1.2, 3.4]).sum(Math.floor) // => 4 Lazy(['foo', 'bar']).sum('length') // => 6
takeWhile Sequence#takeWhile
Creates a new sequence comprising the elements from the head of this sequence that satisfy some predicate. Once an element is encountered that doesn't satisfy the predicate, iteration will stop.
Signature
Sequence.takeWhile = function(predicate) { /*...*/ }
Sequence.takeWhile = function takeWhile(predicate) { return new TakeWhileSequence(this, predicate); }
Name | Type(s) | Description |
---|---|---|
predicate | Function | |
returns | Sequence | The new sequence |
Examples
function lessThan(x) { return function(y) { return y < x; }; } Lazy([1, 2, 3, 4]).takeWhile(lessThan(3)) // sequence: [1, 2] Lazy([1, 2, 3, 4]).takeWhile(lessThan(0)) // sequence: []
tap Sequence#tap
Passes each element in the sequence to the specified callback during iteration. This is like Sequence#each, except that it can be inserted anywhere in the middle of a chain of methods to "intercept" the values in the sequence at that point.
Signature
Sequence.tap = function(callback) { /*...*/ }
Sequence.tap = function tap(callback) { return new TappedSequence(this, callback); }
Name | Type(s) | Description |
---|---|---|
callback | Function | A function to call on every element in the sequence during iteration. The return value of this function does not matter. |
returns | Sequence | A sequence comprising the same elements as this one. |
Examples
Lazy([1, 2, 3]).tap(fn).each(Lazy.noop); // calls fn 3 times
toArray Sequence#toArray
Creates an array snapshot of a sequence.
Note that for indefinite sequences, this method may raise an exception or (worse) cause the environment to hang.
Signature
Sequence.toArray = function() { /*...*/ }
Sequence.toArray = function toArray() { return this.reduce(function(arr, element) { arr.push(element); return arr; }, []); }
Name | Type(s) | Description |
---|---|---|
returns | Array | An array containing the current contents of the sequence. |
Examples
Lazy([1, 2, 3]).toArray() // => [1, 2, 3]
toObject Sequence#toObject
Creates an object from a sequence of key/value pairs.
Signature
Sequence.toObject = function() { /*...*/ }
Sequence.toObject = function toObject() { return this.reduce(function(object, pair) { object[pair[0]] = pair[1]; return object; }, {}); }
Name | Type(s) | Description |
---|---|---|
returns | Object | An object with keys and values corresponding to the pairs of elements in the sequence. |
Examples
var details = [ ["first", "Dan"], ["last", "Tao"], ["age", 29] ]; Lazy(details).toObject() // => { first: "Dan", last: "Tao", age: 29 }
union Sequence#union
Creates a new sequence with all the unique elements either in this sequence or among the specified arguments.
Signature
Sequence.union = function(var_args) { /*...*/ }
Sequence.union = function union(var_args) { return this.concat(var_args).uniq(); }
Name | Type(s) | Description |
---|---|---|
var_args | ...* | The values, or array(s) of values, to be additionally included in the resulting sequence. |
returns | Sequence | The new sequence. |
Examples
Lazy(["foo", "bar"]).union([]) // sequence: ["foo", "bar"] Lazy(["foo", "bar"]).union(["bar", "baz"]) // sequence: ["foo", "bar", "baz"]
uniq
Sequence#uniq
aliases:
unique
Creates a new sequence with every unique element from this one appearing exactly once (i.e., with duplicates removed).
Signature
Sequence.uniq = function(keyFn) { /*...*/ }
Sequence.uniq = function uniq(keyFn) { return new UniqueSequence(this, keyFn); }
Name | Type(s) | Description |
---|---|---|
keyFn | Function? | An optional function to produce the key for each object. This key is then tested for uniqueness as opposed to the object reference. |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 2, 3, 3, 3]).uniq() // sequence: [1, 2, 3] Lazy([{ name: 'mike' }, { name: 'sarah' }, { name: 'mike' } ]).uniq('name') // sequence: [{ name: 'mike' }, { name: 'sarah' }]
Benchmarks
function randomOf(array) { return function() { return array[Math.floor(Math.random() * array.length)]; }; } var mostUnique = Lazy.generate(randomOf(_.range(100)), 100).toArray(), someUnique = Lazy.generate(randomOf(_.range(50)), 100).toArray(), mostDupes = Lazy.generate(randomOf(_.range(5)), 100).toArray(); Lazy(mostUnique).uniq().each(Lazy.noop) // lazy - mostly unique elements Lazy(someUnique).uniq().each(Lazy.noop) // lazy - some unique elements Lazy(mostDupes).uniq().each(Lazy.noop) // lazy - mostly duplicate elements _.each(_.uniq(mostUnique), _.noop) // lodash - mostly unique elements _.each(_.uniq(someUnique), _.noop) // lodash - some unique elements _.each(_.uniq(mostDupes), _.noop) // lodash - mostly duplicate elements
Implementation | mostly unique elements | some unique elements | mostly duplicate elements |
---|---|---|---|
lazy | |||
lodash |
where Sequence#where
Creates a new sequence whose values are the elements of this sequence with property names and values matching those of the specified object.
Signature
Sequence.where = function(properties) { /*...*/ }
Sequence.where = function where(properties) { return this.filter(properties); }
Name | Type(s) | Description |
---|---|---|
properties | Object | The properties that should be found on every element that is to be included in this sequence. |
returns | Sequence | The new sequence. |
Examples
var people = [ { first: "Dan", last: "Tao" }, { first: "Bob", last: "Smith" } ]; Lazy(people).where({ first: "Dan" }) // sequence: [{ first: "Dan", last: "Tao" }]
Benchmarks
var animals = ["dog", "cat", "mouse", "horse", "pig", "snake"]; Lazy(animals).where({ length: 3 }).each(Lazy.noop) // lazy _.each(_.where(animals, { length: 3 }), _.noop) // lodash
Implementation | Ops/second |
---|---|
lazy | |
lodash |
without
Sequence#without
aliases:
difference
Creates a new sequence with all the elements of this sequence that are not also among the specified arguments.
Signature
Sequence.without = function(var_args) { /*...*/ }
Sequence.without = function without(var_args) { return new WithoutSequence(this, arraySlice.call(arguments, 0)); }
Name | Type(s) | Description |
---|---|---|
var_args | ...* | The values, or array(s) of values, to be excluded from the resulting sequence. |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2, 3, 4, 5]).without(2, 3) // sequence: [1, 4, 5] Lazy([1, 2, 3, 4, 5]).without([4, 5]) // sequence: [1, 2, 3]
zip Sequence#zip
Creates a new sequence by combining the elements from this sequence with corresponding elements from the specified array(s).
Signature
Sequence.zip = function(var_args) { /*...*/ }
Sequence.zip = function zip(var_args) { if (arguments.length === 1) { return new SimpleZippedSequence(this, (/** @type {Array} */ var_args)); } else { return new ZippedSequence(this, arraySlice.call(arguments, 0)); } }
Name | Type(s) | Description |
---|---|---|
var_args | ...Array | One or more arrays of elements to combine with those of this sequence. |
returns | Sequence | The new sequence. |
Examples
Lazy([1, 2]).zip([3, 4]) // sequence: [[1, 3], [2, 4]] Lazy([]).zip([0]) // sequence: [[undefined, 0]] Lazy([0]).zip([]) // sequence: [[0, undefined]] Lazy([]).zip([1, 2], [3, 4]) // sequence: [[undefined, 1, 3], [undefined, 2, 4]] Lazy([]).zip([1], [2, 3]) // sequence: [[undefined, 1, 2], [undefined, undefined, 3]] Lazy([1, 2]).zip([3], [4]) // sequence: [[1, 3, 4], [2, undefined, undefined]]
Benchmarks
var smArrL = Lazy.range(10).toArray(), smArrR = Lazy.range(10, 20).toArray(), lgArrL = Lazy.range(100).toArray(), lgArrR = Lazy.range(100, 200).toArray(); Lazy(smArrL).zip(smArrR).each(Lazy.noop) // lazy - zipping 10-element arrays Lazy(lgArrL).zip(lgArrR).each(Lazy.noop) // lazy - zipping 100-element arrays _.each(_.zip(smArrL, smArrR), _.noop) // lodash - zipping 10-element arrays _.each(_.zip(lgArrL, lgArrR), _.noop) // lodash - zipping 100-element arrays
Implementation | zipping 10-element arrays | zipping 100-element arrays |
---|---|---|
lazy | ||
lodash |
ArrayLikeSequence ArrayLikeSequence
An ArrayLikeSequence
is a Sequence that provides random access to
its elements. This extends the API for iterating with the additional methods
#get and #length, allowing a sequence to act as a "view" into
a collection or other indexed data source.
The initial sequence created by wrapping an array with Lazy(array)
is an
ArrayLikeSequence
.
All methods of ArrayLikeSequence
that conceptually should return
something like a array (with indexed access) return another
ArrayLikeSequence
, for example:
The above is not an exhaustive list. There are also certain other cases
where it might be possible to return an ArrayLikeSequence
(e.g., calling
Sequence#concat with a single array argument), but this is not
guaranteed by the API.
Note that in many cases, it is not possible to provide indexed access
without first performing at least a partial iteration of the underlying
sequence. In these cases an ArrayLikeSequence
will not be returned:
etc. The above methods only return ordinary Sequence objects.
Defining custom array-like sequences
Creating a custom ArrayLikeSequence
is essentially the same as creating a
custom Sequence. You just have a couple more methods you need to
implement: get
and (optionally) length
.
Here's an example. Let's define a sequence type called OffsetSequence
that
offsets each of its parent's elements by a set distance, and circles back to
the beginning after reaching the end. Remember: the initialization
function you pass to #define should always accept a parent
as its
first parameter.
ArrayLikeSequence.define("offset", {
init: function(parent, offset) {
this.offset = offset;
},
get: function(i) {
return this.parent.get((i + this.offset) % this.parent.length());
}
});
It's worth noting a couple of things here.
First, Lazy's default implementation of length
simply returns the parent's
length. In this case, since an OffsetSequence
will always have the same
number of elements as its parent, that implementation is fine; so we don't
need to override it.
Second, the default implementation of each
uses get
and length
to
essentially create a for
loop, which is fine here. If you want to implement
each
your own way, you can do that; but in most cases (as here), you can
probably just stick with the default.
So we're already done, after only implementing get
! Pretty easy, huh?
Now the offset
method will be chainable from any ArrayLikeSequence
. So
for example:
Lazy([1, 2, 3]).map(mapFn).offset(3);
...will work, but:
Lazy([1, 2, 3]).filter(mapFn).offset(3);
...will not (because filter
does not return an ArrayLikeSequence
).
(Also, as with the example provided for defining custom Sequence types, this example really could have been implemented using a function already available as part of Lazy.js: in this case, Sequence#map.)
Examples
Lazy([1, 2, 3]) // instanceof Lazy.ArrayLikeSequence Lazy([1, 2, 3]).map(Lazy.identity) // instanceof Lazy.ArrayLikeSequence Lazy([1, 2, 3]).take(2) // instanceof Lazy.ArrayLikeSequence Lazy([1, 2, 3]).drop(2) // instanceof Lazy.ArrayLikeSequence Lazy([1, 2, 3]).reverse() // instanceof Lazy.ArrayLikeSequence Lazy([1, 2, 3]).slice(1, 2) // instanceof Lazy.ArrayLikeSequence
define ArrayLikeSequence.define
Create a new constructor function for a type inheriting from
ArrayLikeSequence
.
Signature
ArrayLikeSequence.define = function(methodName, overrides) { /*...*/ }
ArrayLikeSequence.define = function define(methodName, overrides) { if (!overrides || typeof overrides.get !== 'function') { throw new Error("A custom array-like sequence must implement *at least* get!"); } return defineSequenceType(ArrayLikeSequence, methodName, overrides); }
Name | Type(s) | Description |
---|---|---|
methodName | string|Array.<string> | The name(s) of the method(s) to be
used for constructing the new sequence. The method will be attached to
the |
overrides | Object | An object containing function overrides for this
new sequence type. Must include |
returns | Function | A constructor for a new type inheriting from
|
Examples
Lazy.ArrayLikeSequence.define("offset", { init: function(offset) { this.offset = offset; }, get: function(i) { return this.parent.get((i + this.offset) % this.parent.length()); } }); Lazy([1, 2, 3]).offset(1) // sequence: [2, 3, 1]
concat ArrayLikeSequence#concat
An optimized version of Sequence#concat that returns another ArrayLikeSequence if the argument is an array.
Signature
ArrayLikeSequence.concat = function(var_args) { /*...*/ }
ArrayLikeSequence.concat = function concat(var_args) { if (arguments.length === 1 && isArray(arguments[0])) { return new IndexedConcatenatedSequence(this, (/** @type {Array} */ var_args)); } else { return Sequence.prototype.concat.apply(this, arguments); } }
Name | Type(s) | Description |
---|---|---|
var_args | ...* |
Examples
Lazy([1, 2]).concat([3, 4]) // instanceof Lazy.ArrayLikeSequence Lazy([1, 2]).concat([3, 4]) // sequence: [1, 2, 3, 4]
first ArrayLikeSequence#first
An optimized version of Sequence#first, which creates an ArrayLikeSequence so that the result still provides random access.
Examples
Lazy([1, 2, 3]).first(2) // instanceof Lazy.ArrayLikeSequence
get ArrayLikeSequence#get
Returns the element at the specified index.
Signature
ArrayLikeSequence.get = function(i) { /*...*/ }
ArrayLikeSequence.get = function get(i) { return this.parent.get(i); }
Name | Type(s) | Description |
---|---|---|
i | number | The index to access. |
returns | * | The element. |
Examples
function increment(x) { return x + 1; } Lazy([1, 2, 3]).get(1) // => 2 Lazy([1, 2, 3]).get(-1) // => undefined Lazy([1, 2, 3]).map(increment).get(1) // => 3
length ArrayLikeSequence#length
Returns the length of the sequence.
Signature
ArrayLikeSequence.length = function() { /*...*/ }
ArrayLikeSequence.length = function length() { return this.parent.length(); }
Name | Type(s) | Description |
---|---|---|
returns | number | The length. |
Examples
function increment(x) { return x + 1; } Lazy([]).length() // => 0 Lazy([1, 2, 3]).length() // => 3 Lazy([1, 2, 3]).map(increment).length() // => 3
map ArrayLikeSequence#map
An optimized version of Sequence#map, which creates an ArrayLikeSequence so that the result still provides random access.
Examples
Lazy([1, 2, 3]).map(Lazy.identity) // instanceof Lazy.ArrayLikeSequence
pop ArrayLikeSequence#pop
Returns a new sequence with the same elements as this one, minus the last element.
Signature
ArrayLikeSequence.pop = function() { /*...*/ }
ArrayLikeSequence.pop = function pop() { return this.initial(); }
Name | Type(s) | Description |
---|---|---|
returns | ArrayLikeSequence | The new array-like sequence. |
Examples
Lazy([1, 2, 3]).pop() // sequence: [1, 2] Lazy([]).pop() // sequence: []
push ArrayLikeSequence#push
Returns a new sequence with the same elements as this one, plus the specified element at the end.
Signature
ArrayLikeSequence.push = function() { /*...*/ }
ArrayLikeSequence.push = function push(value) { return this.concat([value]); }
Name | Type(s) | Description |
---|---|---|
returns | ArrayLikeSequence | The new array-like sequence. |
Examples
Lazy([1, 2]).push(3) // sequence: [1, 2, 3] Lazy([]).push(1) // sequence: [1]
rest ArrayLikeSequence#rest
An optimized version of Sequence#rest, which creates an ArrayLikeSequence so that the result still provides random access.
Examples
Lazy([1, 2, 3]).rest() // instanceof Lazy.ArrayLikeSequence
reverse ArrayLikeSequence#reverse
An optimized version of Sequence#reverse, which creates an ArrayLikeSequence so that the result still provides random access.
Examples
Lazy([1, 2, 3]).reverse() // instanceof Lazy.ArrayLikeSequence
shift ArrayLikeSequence#shift
Returns a new sequence with the same elements as this one, minus the first element.
Signature
ArrayLikeSequence.shift = function() { /*...*/ }
ArrayLikeSequence.shift = function shift() { return this.drop(); }
Name | Type(s) | Description |
---|---|---|
returns | ArrayLikeSequence | The new array-like sequence. |
Examples
Lazy([1, 2, 3]).shift() // sequence: [2, 3] Lazy([]).shift() // sequence: []
slice ArrayLikeSequence#slice
Returns a new sequence comprising the portion of this sequence starting from the specified starting index and continuing until the specified ending index or to the end of the sequence.
Signature
ArrayLikeSequence.slice = function(begin, end) { /*...*/ }
ArrayLikeSequence.slice = function slice(begin, end) { var length = this.length(); if (begin < 0) { begin = length + begin; } var result = this.drop(begin); if (typeof end === "number") { if (end < 0) { end = length + end; } result = result.take(end - begin); } return result; }
Name | Type(s) | Description |
---|---|---|
begin | number | The index at which the new sequence should start. |
end | number? | The index at which the new sequence should end. |
returns | ArrayLikeSequence | The new array-like sequence. |
Examples
Lazy([1, 2, 3, 4, 5]).slice(0) // sequence: [1, 2, 3, 4, 5] Lazy([1, 2, 3, 4, 5]).slice(2) // sequence: [3, 4, 5] Lazy([1, 2, 3, 4, 5]).slice(2, 4) // sequence: [3, 4] Lazy([1, 2, 3, 4, 5]).slice(-1) // sequence: [5] Lazy([1, 2, 3, 4, 5]).slice(1, -1) // sequence: [2, 3, 4] Lazy([1, 2, 3, 4, 5]).slice(0, 10) // sequence: [1, 2, 3, 4, 5]
unshift ArrayLikeSequence#unshift
Returns a new sequence with the same elements as this one, plus the specified element at the beginning.
Signature
ArrayLikeSequence.unshift = function() { /*...*/ }
ArrayLikeSequence.unshift = function unshift(value) { return Lazy([value]).concat(this); }
Name | Type(s) | Description |
---|---|---|
returns | ArrayLikeSequence | The new array-like sequence. |
Examples
Lazy([1, 2]).unshift(3) // sequence: [3, 1, 2] Lazy([]).unshift(1) // sequence: [1]
ObjectLikeSequence ObjectLikeSequence
An ObjectLikeSequence
object represents a sequence of key/value pairs.
The initial sequence you get by wrapping an object with Lazy(object)
is
an ObjectLikeSequence
.
All methods of ObjectLikeSequence
that conceptually should return
something like an object return another ObjectLikeSequence
.
Examples
var obj = { foo: 'bar' }; Lazy(obj).assign({ bar: 'baz' }) // instanceof Lazy.ObjectLikeSequence Lazy(obj).defaults({ bar: 'baz' }) // instanceof Lazy.ObjectLikeSequence Lazy(obj).invert() // instanceof Lazy.ObjectLikeSequence
define ObjectLikeSequence.define
Create a new constructor function for a type inheriting from
ObjectLikeSequence
.
Signature
ObjectLikeSequence.define = function(methodName, overrides) { /*...*/ }
ObjectLikeSequence.define = function define(methodName, overrides) { if (!overrides || typeof overrides.each !== 'function') { throw new Error("A custom object-like sequence must implement *at least* each!"); } return defineSequenceType(ObjectLikeSequence, methodName, overrides); }
Name | Type(s) | Description |
---|---|---|
methodName | string|Array.<string> | The name(s) of the method(s) to be
used for constructing the new sequence. The method will be attached to
the |
overrides | Object | An object containing function overrides for this
new sequence type. Must include |
returns | Function | A constructor for a new type inheriting from
|
Examples
function downcaseKey(value, key) { return [key.toLowerCase(), value]; } Lazy.ObjectLikeSequence.define("caseInsensitive", { init: function() { var downcased = this.parent .map(downcaseKey) .toObject(); this.downcased = Lazy(downcased); }, get: function(key) { return this.downcased.get(key.toLowerCase()); }, each: function(fn) { return this.downcased.each(fn); } }); Lazy({ Foo: 'bar' }).caseInsensitive() // sequence: { foo: 'bar' } Lazy({ FOO: 'bar' }).caseInsensitive().get('foo') // => 'bar' Lazy({ FOO: 'bar' }).caseInsensitive().get('FOO') // => 'bar'
assign
ObjectLikeSequence#assign
aliases:
extend
Returns an ObjectLikeSequence whose elements are the combination of this sequence and another object. In the case of a key appearing in both this sequence and the given object, the other object's value will override the one in this sequence.
Signature
ObjectLikeSequence.assign = function(other) { /*...*/ }
ObjectLikeSequence.assign = function assign(other) { return new AssignSequence(this, other); }
Name | Type(s) | Description |
---|---|---|
other | Object | The other object to assign to this sequence. |
returns | ObjectLikeSequence | A new sequence comprising elements from this
sequence plus the contents of |
Examples
Lazy({ "uno": 1, "dos": 2 }).assign({ "tres": 3 }) // sequence: { uno: 1, dos: 2, tres: 3 } Lazy({ foo: "bar" }).assign({ foo: "baz" }); // sequence: { foo: "baz" } Lazy({ foo: 'foo' }).assign({ foo: false }).get('foo') // false
async ObjectLikeSequence#async
Throws an exception. Asynchronous iteration over object-like sequences is not supported.
Examples
Lazy({ foo: 'bar' }).async() // throws
defaults ObjectLikeSequence#defaults
Returns an ObjectLikeSequence whose elements are the combination of this sequence and a 'default' object. In the case of a key appearing in both this sequence and the given object, this sequence's value will override the default object's.
Signature
ObjectLikeSequence.defaults = function(defaults) { /*...*/ }
ObjectLikeSequence.defaults = function defaults(defaults) { return new DefaultsSequence(this, defaults); }
Name | Type(s) | Description |
---|---|---|
defaults | Object | The 'default' object to use for missing keys in this sequence. |
returns | ObjectLikeSequence | A new sequence comprising elements from this
sequence supplemented by the contents of |
Examples
Lazy({ name: "Dan" }).defaults({ name: "User", password: "passw0rd" }) // sequence: { name: "Dan", password: "passw0rd" } Lazy({ foo: false }).defaults({ foo: 'foo' }).get('foo') // false Lazy({ a: 1 }).defaults({ b: 2 }).defaults({ c: 3 }) // sequence: { a: 1, b: 2, c: 3 } Lazy({ a: 1 }).defaults({ b: 2 }).defaults({ a: 3 }) // sequence: { a: 1, b: 2 } Lazy({ a: 1, b: 2 }).defaults({ b: 5 }).defaults({ c: 3, d: 4 }) // sequence: { a: 1, b: 2, c: 3, d: 4 }
functions
ObjectLikeSequence#functions
aliases:
methods
Creates a Sequence consisting of the keys from this sequence whose values are functions.
Signature
ObjectLikeSequence.functions = function() { /*...*/ }
ObjectLikeSequence.functions = function functions() { return this .filter(function(v, k) { return typeof(v) === "function"; }) .map(function(v, k) { return k; }); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The new sequence. |
Examples
var dog = { name: "Fido", breed: "Golden Retriever", bark: function() { console.log("Woof!"); }, wagTail: function() { console.log("TODO: implement robotic dog interface"); } }; Lazy(dog).functions() // sequence: ["bark", "wagTail"]
get ObjectLikeSequence#get
Gets the element at the specified key in this sequence.
Signature
ObjectLikeSequence.get = function(key) { /*...*/ }
ObjectLikeSequence.get = function get(key) { var pair = this.pairs().find(function(pair) { return pair[0] === key; }); return pair ? pair[1] : undefined; }
Name | Type(s) | Description |
---|---|---|
key | string | The key. |
returns | * | The element. |
Examples
Lazy({ foo: "bar" }).get("foo") // => "bar" Lazy({ foo: "bar" }).extend({ foo: "baz" }).get("foo") // => "baz" Lazy({ foo: "bar" }).defaults({ bar: "baz" }).get("bar") // => "baz" Lazy({ foo: "bar" }).invert().get("bar") // => "foo" Lazy({ foo: 1, bar: 2 }).pick(["foo"]).get("foo") // => 1 Lazy({ foo: 1, bar: 2 }).pick(["foo"]).get("bar") // => undefined Lazy({ foo: 1, bar: 2 }).omit(["foo"]).get("bar") // => 2 Lazy({ foo: 1, bar: 2 }).omit(["foo"]).get("foo") // => undefined
invert ObjectLikeSequence#invert
Returns an ObjectLikeSequence whose values are this sequence's keys, and whose keys are this sequence's values.
Signature
ObjectLikeSequence.invert = function() { /*...*/ }
ObjectLikeSequence.invert = function invert() { return new InvertedSequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | ObjectLikeSequence | A new sequence comprising the inverted keys and values from this sequence. |
Examples
Lazy({ first: "Dan", last: "Tao" }).invert() // sequence: { Dan: "first", Tao: "last" }
keys ObjectLikeSequence#keys
Returns a Sequence whose elements are the keys of this object-like sequence.
Signature
ObjectLikeSequence.keys = function() { /*...*/ }
ObjectLikeSequence.keys = function keys() { return new KeySequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The sequence based on this sequence's keys. |
Examples
var obj = { hello: "hola", goodbye: "hasta luego" }; Lazy(obj).keys() // sequence: ["hello", "goodbye"] Lazy(obj).keys().map(function(v, i) { return [v, i]; }) // sequence: [["hello", 0], ["goodbye", 1]]
merge ObjectLikeSequence#merge
Produces an ObjectLikeSequence consisting of all the recursively merged values from this and the given object(s) or sequence(s).
Note that by default this method only merges "vanilla" objects (bags of key/value pairs), not arrays or any other custom object types. To customize how merging works, you can provide the mergeFn argument, e.g. to handling merging arrays, strings, or other types of objects.
Signature
ObjectLikeSequence.merge = function(others, mergeFn) { /*...*/ }
ObjectLikeSequence.merge = function merge(var_args) { var mergeFn = arguments.length > 1 && typeof arguments[arguments.length - 1] === "function" ? arrayPop.call(arguments) : null; return new MergedSequence(this, arraySlice.call(arguments, 0), mergeFn); }
Name | Type(s) | Description |
---|---|---|
others | ...Object|ObjectLikeSequence | The other object(s) or sequence(s) whose values will be merged into this one. |
mergeFn | Function? | An optional function used to customize merging behavior. The function should take two values as parameters and return whatever the "merged" form of those values is. If the function returns undefined then the new value will simply replace the old one in the final result. |
returns | ObjectLikeSequence | The new sequence consisting of merged values. |
Examples
// These examples are completely stolen from Lo-Dash's documentation: // lodash.com/docs#merge var names = { 'characters': [ { 'name': 'barney' }, { 'name': 'fred' } ] }; var ages = { 'characters': [ { 'age': 36 }, { 'age': 40 } ] }; var food = { 'fruits': ['apple'], 'vegetables': ['beet'] }; var otherFood = { 'fruits': ['banana'], 'vegetables': ['carrot'] }; function mergeArrays(a, b) { return Array.isArray(a) ? a.concat(b) : undefined; } Lazy(names).merge(ages); // => sequence: { 'characters': [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }] } Lazy(food).merge(otherFood, mergeArrays); // => sequence: { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot'] } // ----- Now for my own tests: ----- // merges objects Lazy({ foo: 1 }).merge({ foo: 2 }); // => sequence: { foo: 2 } Lazy({ foo: 1 }).merge({ bar: 2 }); // => sequence: { foo: 1, bar: 2 } // goes deep Lazy({ foo: { bar: 1 } }).merge({ foo: { bar: 2 } }); // => sequence: { foo: { bar: 2 } } Lazy({ foo: { bar: 1 } }).merge({ foo: { baz: 2 } }); // => sequence: { foo: { bar: 1, baz: 2 } } Lazy({ foo: { bar: 1 } }).merge({ foo: { baz: 2 } }); // => sequence: { foo: { bar: 1, baz: 2 } } // gives precedence to later sources Lazy({ foo: 1 }).merge({ bar: 2 }, { bar: 3 }); // => sequence: { foo: 1, bar: 3 } // undefined gets passed over Lazy({ foo: 1 }).merge({ foo: undefined }); // => sequence: { foo: 1 } // null doesn't get passed over Lazy({ foo: 1 }).merge({ foo: null }); // => sequence: { foo: null } // array contents get merged as well Lazy({ foo: [{ bar: 1 }] }).merge({ foo: [{ baz: 2 }] }); // => sequence: { foo: [{ bar: 1, baz: 2}] }
omit ObjectLikeSequence#omit
Creates an ObjectLikeSequence consisting of the key/value pairs from this sequence excluding those with the specified keys. Non-string keys are effectively ignored.
Signature
ObjectLikeSequence.omit = function(properties) { /*...*/ }
ObjectLikeSequence.omit = function omit(properties) { return new OmitSequence(this, properties); }
Name | Type(s) | Description |
---|---|---|
properties | Array | An array of the properties to omit from this sequence. |
returns | ObjectLikeSequence | The new sequence. |
Examples
var players = { "who": "first", "what": "second", "i don't know": "third" }; Lazy(players).omit(["who", "what"]) // sequence: { "i don't know": "third" } // Example to show handling of non-string keys Lazy({1: 2, true: false}).omit([1, true]) // sequence: { "1": 2, "true": false }
pairs
ObjectLikeSequence#pairs
aliases:
toArray
Maps the key/value pairs in this sequence to arrays.
Signature
ObjectLikeSequence.pairs = function() { /*...*/ }
ObjectLikeSequence.pairs = function pairs() { return this.map(function(v, k) { return [k, v]; }); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | An sequence of |
Examples
var colorCodes = { red: "#f00", green: "#0f0", blue: "#00f" }; Lazy(colorCodes).pairs() // sequence: [["red", "#f00"], ["green", "#0f0"], ["blue", "#00f"]]
pick ObjectLikeSequence#pick
Creates an ObjectLikeSequence consisting of the key/value pairs from this sequence whose keys are included in the given array of property names.
Signature
ObjectLikeSequence.pick = function(properties) { /*...*/ }
ObjectLikeSequence.pick = function pick(properties) { return new PickSequence(this, properties); }
Name | Type(s) | Description |
---|---|---|
properties | Array.<string> | An array of the properties to "pick" from this sequence. |
returns | ObjectLikeSequence | The new sequence. |
Examples
var players = { "who": "first", "what": "second", "i don't know": "third" }; Lazy(players).pick(["who", "what"]) // sequence: { who: "first", what: "second" }
toArray ObjectLikeSequence#toArray
Creates an array from the key/value pairs in this sequence.
Signature
ObjectLikeSequence.toArray = function() { /*...*/ }
ObjectLikeSequence.toArray = function toArray() { return this.pairs().toArray(); }
Name | Type(s) | Description |
---|---|---|
returns | Array | An array of |
Examples
var colorCodes = { red: "#f00", green: "#0f0", blue: "#00f" }; Lazy(colorCodes).toArray() // => [["red", "#f00"], ["green", "#0f0"], ["blue", "#00f"]]
toObject ObjectLikeSequence#toObject
Creates an object with the key/value pairs from this sequence.
Signature
ObjectLikeSequence.toObject = function() { /*...*/ }
ObjectLikeSequence.toObject = function toObject() { return this.reduce(function(object, value, key) { object[key] = value; return object; }, {}); }
Name | Type(s) | Description |
---|---|---|
returns | Object | An object with the same key/value pairs as this sequence. |
Examples
var colorCodes = { red: "#f00", green: "#0f0", blue: "#00f" }; Lazy(colorCodes).toObject() // => { red: "#f00", green: "#0f0", blue: "#00f" }
values ObjectLikeSequence#values
Returns a Sequence whose elements are the values of this object-like sequence.
Signature
ObjectLikeSequence.values = function() { /*...*/ }
ObjectLikeSequence.values = function values() { return new ValuesSequence(this); }
Name | Type(s) | Description |
---|---|---|
returns | Sequence | The sequence based on this sequence's values. |
Examples
Lazy({ hello: "hola", goodbye: "hasta luego" }).values() // sequence: ["hola", "hasta luego"]
watch ObjectLikeSequence#watch
Watches for all changes to a specified property (or properties) of an
object and produces a sequence whose elements have the properties
{ property, value }
indicating which property changed and what it was
changed to.
Note that this method only works on directly wrapped objects; it will not work on any arbitrary ObjectLikeSequence.
Signature
ObjectLikeSequence.watch = function(propertyNames) { /*...*/ }
ObjectLikeSequence.watch = function watch(propertyNames) { throw new Error('You can only call #watch on a directly wrapped object.'); }
Name | Type(s) | Description |
---|---|---|
propertyNames | string|Array? | A property name or array of property
names to watch. If this parameter is |
returns | Sequence | A sequence comprising |
Examples
var obj = {}, changes = []; Lazy(obj).watch('foo').each(function(change) { changes.push(change); }); obj.foo = 1; obj.bar = 2; obj.foo = 3; obj.foo; // => 3 changes; // => [{ property: 'foo', value: 1 }, { property: 'foo', value: 3 }]
StringLikeSequence StringLikeSequence
A StringLikeSequence
represents a sequence of characters.
The initial sequence you get by wrapping a string with Lazy(string)
is a
StringLikeSequence
.
All methods of StringLikeSequence
that conceptually should return
something like a string return another StringLikeSequence
.
Examples
function upcase(str) { return str.toUpperCase(); } Lazy('foo') // instanceof Lazy.StringLikeSequence Lazy('foo').toUpperCase() // instanceof Lazy.StringLikeSequence Lazy('foo').reverse() // instanceof Lazy.StringLikeSequence Lazy('foo').take(2) // instanceof Lazy.StringLikeSequence Lazy('foo').drop(1) // instanceof Lazy.StringLikeSequence Lazy('foo').substring(1) // instanceof Lazy.StringLikeSequence // Note that `map` does not create a `StringLikeSequence` because there's // no guarantee the mapping function will return characters. In the event // you do want to map a string onto a string-like sequence, use // `mapString`: Lazy('foo').map(Lazy.identity) // instanceof Lazy.ArrayLikeSequence Lazy('foo').mapString(Lazy.identity) // instanceof Lazy.StringLikeSequence
define StringLikeSequence.define
Create a new constructor function for a type inheriting from
StringLikeSequence
.
Signature
StringLikeSequence.define = function(methodName, overrides) { /*...*/ }
StringLikeSequence.define = function define(methodName, overrides) { if (!overrides || typeof overrides.get !== 'function') { throw new Error("A custom string-like sequence must implement *at least* get!"); } return defineSequenceType(StringLikeSequence, methodName, overrides); }
Name | Type(s) | Description |
---|---|---|
methodName | string|Array.<string> | The name(s) of the method(s) to be
used for constructing the new sequence. The method will be attached to
the |
overrides | Object | An object containing function overrides for this new sequence type. Has the same requirements as ArrayLikeSequence.define. |
returns | Function | A constructor for a new type inheriting from
|
Examples
Lazy.StringLikeSequence.define("zomg", { length: function() { return this.parent.length() + "!!ZOMG!!!1".length; }, get: function(i) { if (i < this.parent.length()) { return this.parent.get(i); } return "!!ZOMG!!!1".charAt(i - this.parent.length()); } }); Lazy('foo').zomg() // sequence: "foo!!ZOMG!!!1"
charAt StringLikeSequence#charAt
Returns the character at the given index of this sequence, or the empty string if the specified index lies outside the bounds of the sequence.
Signature
StringLikeSequence.charAt = function(i) { /*...*/ }
StringLikeSequence.charAt = function charAt(i) { return this.get(i); }
Name | Type(s) | Description |
---|---|---|
i | number | The index of this sequence. |
returns | string | The character at the specified index. |
Examples
Lazy("foo").charAt(0) // => "f" Lazy("foo").charAt(-1) // => "" Lazy("foo").charAt(10) // => ""
charCodeAt StringLikeSequence#charCodeAt
Returns the character code at the given index of this sequence, or NaN
if
the index lies outside the bounds of the sequence.
Signature
StringLikeSequence.charCodeAt = function(i) { /*...*/ }
StringLikeSequence.charCodeAt = function charCodeAt(i) { var char = this.charAt(i); if (!char) { return NaN; } return char.charCodeAt(0); }
Name | Type(s) | Description |
---|---|---|
i | number | The index of the character whose character code you want. |
returns | number | The character code. |
Examples
Lazy("abc").charCodeAt(0) // => 97 Lazy("abc").charCodeAt(-1) // => NaN Lazy("abc").charCodeAt(10) // => NaN
contains StringLikeSequence#contains
Checks if this sequence contains a given substring.
Signature
StringLikeSequence.contains = function(substring) { /*...*/ }
StringLikeSequence.contains = function contains(substring) { return this.indexOf(substring) !== -1; }
Name | Type(s) | Description |
---|---|---|
substring | string | The substring to check for. |
returns | boolean | Whether or not this sequence contains |
Examples
Lazy('hello').contains('ell') // => true Lazy('hello').contains('') // => true Lazy('hello').contains('abc') // => false
endsWith StringLikeSequence#endsWith
Checks if this sequence ends with a given suffix.
Signature
StringLikeSequence.endsWith = function(suffix) { /*...*/ }
StringLikeSequence.endsWith = function endsWith(suffix) { return this.substring(this.length() - suffix.length).toString() === suffix; }
Name | Type(s) | Description |
---|---|---|
suffix | string | The suffix to check for. |
returns | boolean | Whether or not this sequence ends with |
Examples
Lazy('foo').endsWith('oo') // => true Lazy('foo').endsWith('') // => true Lazy('foo').endsWith('abc') // => false
first StringLikeSequence#first
An optimized version of Sequence#first that returns another
StringLikeSequence (or just the first character, if count
is
undefined).
Examples
Lazy('foo').first() // => 'f' Lazy('fo').first(2) // sequence: 'fo' Lazy('foo').first(10) // sequence: 'foo' Lazy('foo').toUpperCase().first() // => 'F' Lazy('foo').toUpperCase().first(2) // sequence: 'FO'
indexOf StringLikeSequence#indexOf
Finds the index of the first occurrence of the given substring within this sequence, starting from the specified index (or the beginning of the sequence).
Signature
StringLikeSequence.indexOf = function(substring, startIndex) { /*...*/ }
StringLikeSequence.indexOf = function indexOf(substring, startIndex) { return this.toString().indexOf(substring, startIndex); }
Name | Type(s) | Description |
---|---|---|
substring | string | The substring to search for. |
startIndex | number? | The index from which to start the search. |
returns | number | The first index where the given substring is found, or -1 if it isn't in the sequence. |
Examples
Lazy('canal').indexOf('a') // => 1 Lazy('canal').indexOf('a', 2) // => 3 Lazy('canal').indexOf('ana') // => 1 Lazy('canal').indexOf('andy') // => -1 Lazy('canal').indexOf('x') // => -1
last StringLikeSequence#last
An optimized version of Sequence#last that returns another
StringLikeSequence (or just the last character, if count
is
undefined).
Examples
Lazy('foo').last() // => 'o' Lazy('foo').last(2) // sequence: 'oo' Lazy('foo').last(10) // sequence: 'foo' Lazy('foo').toUpperCase().last() // => 'O' Lazy('foo').toUpperCase().last(2) // sequence: 'OO'
lastIndexOf StringLikeSequence#lastIndexOf
Finds the index of the last occurrence of the given substring within this sequence, starting from the specified index (or the end of the sequence) and working backwards.
Signature
StringLikeSequence.lastIndexOf = function(substring, startIndex) { /*...*/ }
StringLikeSequence.lastIndexOf = function lastIndexOf(substring, startIndex) { return this.toString().lastIndexOf(substring, startIndex); }
Name | Type(s) | Description |
---|---|---|
substring | string | The substring to search for. |
startIndex | number? | The index from which to start the search. |
returns | number | The last index where the given substring is found, or -1 if it isn't in the sequence. |
Examples
Lazy('canal').lastIndexOf('a') // => 3 Lazy('canal').lastIndexOf('a', 2) // => 1 Lazy('canal').lastIndexOf('ana') // => 1 Lazy('canal').lastIndexOf('andy') // => -1 Lazy('canal').lastIndexOf('x') // => -1
mapString StringLikeSequence#mapString
Maps the characters of this sequence onto a new StringLikeSequence.
Signature
StringLikeSequence.mapString = function(mapFn) { /*...*/ }
StringLikeSequence.mapString = function mapString(mapFn) { return new MappedStringLikeSequence(this, mapFn); }
Name | Type(s) | Description |
---|---|---|
mapFn | Function | The function used to map characters from this sequence onto the new sequence. |
returns | StringLikeSequence | The new sequence. |
Examples
function upcase(char) { return char.toUpperCase(); } Lazy("foo").mapString(upcase) // sequence: "FOO" Lazy("foo").mapString(upcase).charAt(0) // => "F" Lazy("foo").mapString(upcase).charCodeAt(0) // => 70 Lazy("foo").mapString(upcase).substring(1) // sequence: "OO"
match StringLikeSequence#match
Creates a Sequence comprising all of the matches for the specified pattern in the underlying string.
Signature
StringLikeSequence.match = function(pattern) { /*...*/ }
StringLikeSequence.match = function match(pattern) { return new StringMatchSequence(this, pattern); }
Name | Type(s) | Description |
---|---|---|
pattern | RegExp | The pattern to match. |
returns | Sequence | A sequence of all the matches. |
Examples
Lazy("abracadabra").match(/a[bcd]/) // sequence: ["ab", "ac", "ad", "ab"] Lazy("fee fi fo fum").match(/\w+/) // sequence: ["fee", "fi", "fo", "fum"] Lazy("hello").match(/xyz/) // sequence: []
reverse StringLikeSequence#reverse
Returns a copy of this sequence that reads back to front.
Examples
Lazy("abcdefg").reverse() // sequence: "gfedcba"
split StringLikeSequence#split
Creates a Sequence comprising all of the substrings of this string separated by the given delimiter, which can be either a string or a regular expression.
Signature
StringLikeSequence.split = function(delimiter) { /*...*/ }
StringLikeSequence.split = function split(delimiter) { return new SplitStringSequence(this, delimiter); }
Name | Type(s) | Description |
---|---|---|
delimiter | string|RegExp | The delimiter to use for recognizing substrings. |
returns | Sequence | A sequence of all the substrings separated by the given delimiter. |
Examples
Lazy("foo").split("") // sequence: ["f", "o", "o"] Lazy("yo dawg").split(" ") // sequence: ["yo", "dawg"] Lazy("bah bah\tblack sheep").split(/\s+/) // sequence: ["bah", "bah", "black", "sheep"]
startsWith StringLikeSequence#startsWith
Checks if this sequence starts with a given prefix.
Signature
StringLikeSequence.startsWith = function(prefix) { /*...*/ }
StringLikeSequence.startsWith = function startsWith(prefix) { return this.substring(0, prefix.length).toString() === prefix; }
Name | Type(s) | Description |
---|---|---|
prefix | string | The prefix to check for. |
returns | boolean | Whether or not this sequence starts with |
Examples
Lazy('foo').startsWith('fo') // => true Lazy('foo').startsWith('') // => true Lazy('foo').startsWith('abc') // => false
substring StringLikeSequence#substring
Returns a StringLikeSequence comprising the characters from this
sequence starting at start
and ending at stop
(exclusive), or---if
stop
is undefined
, including the rest of the sequence.
Signature
StringLikeSequence.substring = function(start, stop) { /*...*/ }
StringLikeSequence.substring = function substring(start, stop) { return new StringSegment(this, start, stop); }
Name | Type(s) | Description |
---|---|---|
start | number | The index where this sequence should begin. |
stop | number? | The index (exclusive) where this sequence should end. |
returns | StringLikeSequence | The new sequence. |
Examples
Lazy("foo").substring(1) // sequence: "oo" Lazy("foo").substring(-1) // sequence: "foo" Lazy("hello").substring(1, 3) // sequence: "el" Lazy("hello").substring(1, 9) // sequence: "ello" Lazy("foo").substring(0, 0) // sequence: "" Lazy("foo").substring(3, 3) // sequence: ""
toLowerCase StringLikeSequence#toLowerCase
Converts all of the characters in this string to lowercase.
Signature
StringLikeSequence.toLowerCase = function() { /*...*/ }
StringLikeSequence.toLowerCase = function toLowerCase() { return this.mapString(function(char) { return char.toLowerCase(); }); }
Name | Type(s) | Description |
---|---|---|
returns | StringLikeSequence | A new sequence with the same characters as this sequence, all lowercase. |
Examples
function nextLetter(a) { return String.fromCharCode(a.charCodeAt(0) + 1); } Lazy('FOO').toLowerCase() // sequence: 'foo' Lazy('FOO').substring(1).toLowerCase() // sequence: 'oo' Lazy('ABC').mapString(nextLetter).toLowerCase() // sequence: 'bcd'
toUpperCase StringLikeSequence#toUpperCase
Converts all of the characters in this string to uppercase.
Signature
StringLikeSequence.toUpperCase = function() { /*...*/ }
StringLikeSequence.toUpperCase = function toUpperCase() { return this.mapString(function(char) { return char.toUpperCase(); }); }
Name | Type(s) | Description |
---|---|---|
returns | StringLikeSequence | A new sequence with the same characters as this sequence, all uppercase. |
Examples
function nextLetter(a) { return String.fromCharCode(a.charCodeAt(0) + 1); } Lazy('foo').toUpperCase() // sequence: 'FOO' Lazy('foo').substring(1).toUpperCase() // sequence: 'OO' Lazy('abc').mapString(nextLetter).toUpperCase() // sequence: 'BCD'
GeneratedSequence GeneratedSequence
A GeneratedSequence
does not wrap an in-memory collection but rather
determines its elements on-the-fly during iteration according to a generator
function.
You create a GeneratedSequence
by calling Lazy.generate.
Signature
function GeneratedSequence(generatorFn, length) { /*...*/ }
function GeneratedSequence(generatorFn, length) { this.get = generatorFn; this.fixedLength = length; }
Name | Type(s) | Description |
---|---|---|
generatorFn | function(number):* | A function which accepts an index and returns a value for the element at that position in the sequence. |
length | number? | The length of the sequence. If this argument is omitted, the sequence will go on forever. |
each GeneratedSequence#each
Iterates over the sequence produced by invoking this sequence's generator
function up to its specified length, or, if length is undefined
,
indefinitely (in which case the sequence will go on forever--you would need
to call, e.g., Sequence#take to limit iteration).
Signature
GeneratedSequence.each = function(fn) { /*...*/ }
GeneratedSequence.each = function each(fn) { var generatorFn = this.get, length = this.fixedLength, i = 0; while (typeof length === "undefined" || i < length) { if (fn(generatorFn(i), i++) === false) { return false; } } return true; }
Name | Type(s) | Description |
---|---|---|
fn | Function | The function to call on each output from the generator function. |
length GeneratedSequence#length
Returns the length of this sequence.
Signature
GeneratedSequence.length = function() { /*...*/ }
GeneratedSequence.length = function length() { return this.fixedLength; }
Name | Type(s) | Description |
---|---|---|
returns | number | The length, or |
AsyncSequence AsyncSequence
An AsyncSequence
iterates over its elements asynchronously when
#each is called.
You get an AsyncSequence
by calling Sequence#async on any
sequence. Note that some sequence types may not support asynchronous
iteration.
Returning values
Because of its asynchronous nature, an AsyncSequence
cannot be used in the
same way as other sequences for functions that return values directly (e.g.,
reduce
, max
, any
, even toArray
).
Instead, these methods return an AsyncHandle
whose onComplete
method
accepts a callback that will be called with the final result once iteration
has finished.
Defining custom asynchronous sequences
There are plenty of ways to define an asynchronous sequence. Here's one.
- First, implement an Iterator. This is an object whose prototype
has the methods Iterator#moveNext (which returns a
boolean
) and current (which returns the current value). - Next, create a simple wrapper that inherits from
AsyncSequence
, whosegetIterator
function returns an instance of the iterator type you just defined.
The default implementation for #each on an AsyncSequence
is to
create an iterator and then asynchronously call Iterator#moveNext
(using setImmediate
, if available, otherwise setTimeout
) until the iterator
can't move ahead any more.
Signature
function AsyncSequence(parent, interval) { /*...*/ }
function AsyncSequence(parent, interval) { if (parent instanceof AsyncSequence) { throw new Error("Sequence is already asynchronous!"); } this.parent = parent; this.interval = interval; this.onNextCallback = getOnNextCallback(interval); this.cancelCallback = getCancelCallback(interval); }
Name | Type(s) | Description |
---|---|---|
parent | Sequence | A Sequence to wrap, to expose asynchronous iteration. |
interval | number? | How many milliseconds should elapse between each element when iterating over this sequence. Note that this interval applies even to the first value in the sequence; i.e., when calling each(), this much time will elapse before the first element is iterated.
|
contains AsyncSequence#contains
A version of Sequence#contains which returns an AsyncHandle.
Signature
AsyncSequence.contains = function(value) { /*...*/ }
AsyncSequence.contains = function contains(value) { var found = false; var handle = this.each(function(e) { if (e === value) { found = true; return false; } }); return handle.then(function() { return found; }); }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
returns | AsyncHandle | An AsyncHandle (promise) which resolves to
either |
each AsyncSequence#each
An asynchronous version of Sequence#each.
Signature
AsyncSequence.each = function(fn) { /*...*/ }
AsyncSequence.each = function each(fn) { var iterator = this.parent.getIterator(), onNextCallback = this.onNextCallback, cancelCallback = this.cancelCallback, i = 0; var handle = new AsyncHandle(function cancel() { if (cancellationId) { cancelCallback(cancellationId); } }); var cancellationId = onNextCallback(function iterate() { cancellationId = null; try { if (iterator.moveNext() && fn(iterator.current(), i++) !== false) { cancellationId = onNextCallback(iterate); } else { handle._resolve(); } } catch (e) { handle._reject(e); } }); return handle; }
Name | Type(s) | Description |
---|---|---|
fn | Function | The function to invoke asynchronously on each element in the sequence one by one. |
returns | AsyncHandle | An AsyncHandle providing the ability to
cancel the asynchronous iteration (by calling |
find AsyncSequence#find
A version of Sequence#find which returns an AsyncHandle.
Signature
AsyncSequence.find = function(predicate) { /*...*/ }
AsyncSequence.find = function find(predicate) { var found; var handle = this.each(function(e, i) { if (predicate(e, i)) { found = e; return false; } }); return handle.then(function() { return found; }); }
Name | Type(s) | Description |
---|---|---|
predicate | Function | A function to call on (potentially) every element in the sequence. |
returns | AsyncHandle | An AsyncHandle (promise) which resolves to
the found element, once it is detected, or else |
getIterator AsyncSequence#getIterator
Throws an exception. You cannot manually iterate over an asynchronous sequence.
Examples
Lazy([1, 2, 3]).async().getIterator() // throws
indexOf AsyncSequence#indexOf
A version of Sequence#indexOf which returns an AsyncHandle.
Signature
AsyncSequence.indexOf = function(value) { /*...*/ }
AsyncSequence.indexOf = function indexOf(value) { var foundIndex = -1; var handle = this.each(function(e, i) { if (e === value) { foundIndex = i; return false; } }); return handle.then(function() { return foundIndex; }); }
Name | Type(s) | Description |
---|---|---|
value | * | The element to search for in the sequence. |
returns | AsyncHandle | An AsyncHandle (promise) which resolves to the found index, once it is detected, or -1. |
Iterator Iterator
The Iterator object provides an API for iterating over a sequence.
The purpose of the Iterator
type is mainly to offer an agnostic way of
iterating over a sequence -- either synchronous (i.e. with a while
loop)
or asynchronously (with recursive calls to either setTimeout
or --- if
available --- setImmediate
). It is not intended to be used directly by
application code.
Signature
function Iterator(sequence) { /*...*/ }
function Iterator(sequence) { this.sequence = sequence; this.index = -1; }
Name | Type(s) | Description |
---|---|---|
sequence | Sequence | The sequence to iterate over. |
current Iterator#current
Gets the current item this iterator is pointing to.
Signature
Iterator.current = function() { /*...*/ }
Iterator.current = function current() { return this.cachedIndex && this.cachedIndex.get(this.index); }
Name | Type(s) | Description |
---|---|---|
returns | * | The current item. |
moveNext Iterator#moveNext
Moves the iterator to the next item in a sequence, if possible.
Signature
Iterator.moveNext = function() { /*...*/ }
Iterator.moveNext = function moveNext() { var cachedIndex = this.cachedIndex; if (!cachedIndex) { cachedIndex = this.cachedIndex = this.sequence.getIndex(); } if (this.index >= cachedIndex.length() - 1) { return false; } ++this.index; return true; }
Name | Type(s) | Description |
---|---|---|
returns | boolean | True if the iterator is able to move to a new item, or else false. |
AsyncHandle AsyncHandle
An AsyncHandle
provides a Promises/A+
compliant interface for an AsyncSequence that is currently (or was)
iterating over its elements.
In addition to behaving as a promise, an AsyncHandle
provides the ability
to AsyncHandle#cancel iteration (if cancelFn
is provided)
and also offers convenient AsyncHandle#onComplete and
AsyncHandle#onError methods to attach listeners for when iteration
is complete or an error is thrown during iteration.
Signature
function AsyncHandle(cancelFn) { /*...*/ }
function AsyncHandle(cancelFn) { this.resolveListeners = []; this.rejectListeners = []; this.state = PENDING; this.cancelFn = cancelFn; }
Name | Type(s) | Description |
---|---|---|
cancelFn | Function | A function to cancel asynchronous iteration. This is passed in to support different cancellation mechanisms for different forms of asynchronous sequences (e.g., timeout-based sequences, sequences based on I/O, etc.). |
Examples
// Create a sequence of 100,000 random numbers, in chunks of 100. var sequence = Lazy.generate(Math.random) .chunk(100) .async() .take(1000); // Reduce-style operations -- i.e., operations that return a *value* (as // opposed to a *sequence*) -- return an AsyncHandle for async sequences. var handle = sequence.toArray(); handle.onComplete(function(array) { // Do something w/ 1,000-element array. }); // Since an AsyncHandle is a promise, you can also use it to create // subsequent promises using `then` (see the Promises/A+ spec for more // info). var flattened = handle.then(function(array) { return Lazy(array).flatten(); });
cancel AsyncHandle#cancel
Cancels asynchronous iteration.
onComplete AsyncHandle#onComplete
Updates the handle with a callback to execute when iteration is completed.
Signature
AsyncHandle.onComplete = function(callback) { /*...*/ }
AsyncHandle.onComplete = function onComplete(callback) { this.resolveListeners.push(callback); return this; }
Name | Type(s) | Description |
---|---|---|
callback | Function | The function to call when the asynchronous iteration is completed. |
onError AsyncHandle#onError
Updates the handle with a callback to execute if/when any error is encountered during asynchronous iteration.
Signature
AsyncHandle.onError = function(callback) { /*...*/ }
AsyncHandle.onError = function onError(callback) { this.rejectListeners.push(callback); return this; }
Name | Type(s) | Description |
---|---|---|
callback | Function | The function to call, with any associated error object, when an error occurs. |