Lazy.js 0.4.2

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.

@dtao

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 (source instanceof Array) {
    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 = new StreamLikeSequence();

  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. this will be bound to the created object, which is an instance of StreamLikeSequence. Use emit to generate data for the sequence.

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:

  1. 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.
  2. The object should include at least either a getIterator method or an each 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 Sequence prototype so that it can be chained with any other sequence methods, like #map, #filter, etc.

overrides Object

An object containing function overrides for this new sequence type. Must include either getIterator or each (or both). May include an init method as well. For these overrides, this will be the new sequence, and this.parent will be the base sequence from which the new sequence was constructed.

returns Function

A constructor for a new type inheriting from Sequence.

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]

consecutive Sequence#consecutive

Groups this sequence into consecutive (overlapping) segments of a specified length. If the underlying sequence has fewer elements than the specfied length, then this sequence will be empty.

Signature

Sequence.consecutive = function(length) { /*...*/ }
Sequence.consecutive = function consecutive(count) {
  var queue    = new Queue(count);
  var segments = this.map(function(element) {
    if (queue.add(element).count === count) {
      return queue.toArray();
    }
  });
  return segments.compact();
}
Name Type(s) Description
length number

The length of each consecutive segment.

returns Sequence

The resulting sequence of consecutive segments.

Examples

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) { /*...*/ }
Sequence.contains = function contains(value) {
  return this.indexOf(value) !== -1;
}
Name Type(s) Description
value *

The element to search for in the sequence.

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

true if the iteration evaluated the entire sequence, or false if iteration was ended early.

Examples

Lazy([1, 2, 3, 4]).each(fn) // calls fn 4 times

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 predicate returns true for every element in the sequence (or the sequence is empty). False if predicate returns false for at least one element.

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 predicate returns true, or undefined if no such element is found.

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 undefined if none exists in this sequence.

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() { /*...*/ }
Sequence.flatten = function flatten() {
  return new FlattenedSequence(this);
}
Name Type(s) Description
returns Sequence

The new sequence.

Examples

Lazy([1, [2, 3], [4, [5]]]).flatten() // 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) { /*...*/ }
Sequence.indexOf = function indexOf(value) {
  var foundIndex = -1;
  this.each(function(e, i) {
    if (e === value) {
      foundIndex = i;
      return false;
    }
  });
  return foundIndex;
}
Name Type(s) Description
value *

The element to search for in the sequence.

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 && arguments[0] instanceof Array) {
    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"]

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 === "string" ? delimiter : ",";

  return this.reduce(function(str, e, i) {
    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

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(",")   // => ",,"

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) {
  var reversed = this.getIndex().reverse(),
      index    = reversed.indexOf(value);
  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(x, y) { return y > x ? y : x; }, -Infinity);
}
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 -Infinity if the sequence is empty.

Examples

function reverseDigits(x) {
  return Number(String(x).split('').reverse().join(''));
}

Lazy([]).max()                              // => -Infinity
Lazy([6, 18, 2, 48, 29]).max()              // => 48
Lazy([6, 18, 2, 48, 29]).max(reverseDigits) // => 29

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(x, y) { return y < x ? y : x; }, Infinity);
}
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 Infinity if the sequence is empty.

Examples

function negate(x) { return x * -1; }

Lazy([]).min()                       // => Infinity
Lazy([6, 18, 2, 49, 34]).min()       // => 2
Lazy([6, 18, 2, 49, 34]).min(negate) // => 49

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 predicate does not return true for any element in the sequence. False if predicate returns true for at least one element.

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 typeof [element] === [type].

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 onComplete method accepts a callback to handle the final result.

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]

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 predicate returns true for at least one element in the sequence. False if predicate returns false for every element (or the sequence is empty).

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:

- 1 if the first is greater
- -1 if the second is greater
- 0 if the two values are the same
descending boolean

Whether or not the resulting sequence should be in descending order (defaults to false).

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 false).

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 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 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]]

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 ArrayLikeSequence prototype so that it can be chained with any other methods that return array-like sequences.

overrides Object

An object containing function overrides for this new sequence type. Must include get. May include init, length, getIterator, and each. For each function, this will be the new sequence and this.parent will be the source sequence.

returns Function

A constructor for a new type inheriting from ArrayLikeSequence.

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 && arguments[0] instanceof Array) {
    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: []

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]

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 ObjectLikeSequence prototype so that it can be chained with any other methods that return object-like sequences.

overrides Object

An object containing function overrides for this new sequence type. Must include each. May include init and get (for looking up an element by key).

returns Function

A constructor for a new type inheriting from ObjectLikeSequence.

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 other.

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" }

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 defaults.

Examples

Lazy({ name: "Dan" }).defaults({ name: "User", password: "passw0rd" }) // sequence: { name: "Dan", password: "passw0rd" }

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 this.map(function(v, k) { return k; });
}
Name Type(s) Description
returns Sequence

The sequence based on this sequence's keys.

Examples

Lazy({ hello: "hola", goodbye: "hasta luego" }).keys() // sequence: ["hello", "goodbye"]

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.

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" }

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 [key, value] pairs.

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

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 [key, value] elements.

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 this.map(function(v, k) { return v; });
}
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 undefined, all of the object's current (enumerable) properties will be watched.

returns Sequence

A sequence comprising { property, value } objects describing each change to the specified property/properties.

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 StringLikeSequence prototype so that it can be chained with any other methods that return string-like sequences.

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 StringLikeSequence.

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 substring.

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 suffix.

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 prefix.

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"

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 colllection 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 undefined if this is an indefinite sequence.

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.

  1. 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).
  2. Next, create a simple wrapper that inherits from AsyncSequence, whose getIterator 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. If this argument is omitted, asynchronous iteration will be executed as fast as possible.

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 true or false to indicate whether the element was found.

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 cancel()) as well as supply callback(s) for when an error is encountered (onError) or when iteration is complete (onComplete).

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 undefined.

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.