jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
I want to test lazy.js against lodash on data validation scripts.
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/3.1.0/lodash.min.js"></script>
<script>
/*
* @name Lazy.js
*
* @fileOverview
* Lazy.js is a lazy evaluation library for JavaScript.
*
* This has been done before. For examples see:
*
* - [wu.js](http://fitzgen.github.io/wu.js/)
* - [Linq.js](http://linqjs.codeplex.com/)
* - [from.js](https://github.com/suckgamoni/fromjs/)
* - [IxJS](http://rx.codeplex.com/)
* - [sloth.js](http://rfw.name/sloth.js/)
*
* 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](http://underscorejs.org/) and
* [Lo-Dash](http://lodash.com/).
*
* Finding your way around the code
* --------------------------------
*
* At the heart of Lazy.js is the {@link Sequence} object. You create an initial
* sequence using {@link 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);
*
* {@link Sequence} is the foundation of other, more specific sequence types.
*
* An {@link ArrayLikeSequence} provides indexed access to its elements.
*
* An {@link ObjectLikeSequence} consists of key/value pairs.
*
* A {@link StringLikeSequence} is like a string (duh): actually, it is an
* {@link ArrayLikeSequence} whose elements happen to be characters.
*
* An {@link 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](http://philosopherdeveloper.com/posts/introducing-lazy-js.html).
*
* You can also [create an issue on GitHub](https://github.com/dtao/lazy.js/issues)
* if you have any issues with the library. I work through them eventually.
*
* [@dtao](https://github.com/dtao)
*/
(function(context) {
/**
* Wraps an object and returns a {@link Sequence}. For `null` or `undefined`,
* simply returns an empty sequence (see {@link Lazy.strict} for a stricter
* implementation).
*
* - For **arrays**, Lazy will create a sequence comprising the elements in
* the array (an {@link ArrayLikeSequence}).
* - For **objects**, Lazy will create a sequence of key/value pairs
* (an {@link ObjectLikeSequence}).
* - For **strings**, Lazy will create a sequence of characters (a
* {@link StringLikeSequence}).
*
* @public
* @param {Array|Object|string} source An array, object, or string to wrap.
* @returns {Sequence} The wrapped lazy object.
*
* @exampleHelpers
* // Utility functions to provide to all examples
* function increment(x) { return x + 1; }
* function isEven(x) { return x % 2 === 0; }
* function isPositive(x) { return x > 0; }
* function isNegative(x) { return x < 0; }
*
* @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: []
*/
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);
}
Lazy.VERSION = '0.3.2';
/*** Utility methods of questionable value ***/
Lazy.noop = function noop() {};
Lazy.identity = function identity(x) { return x; };
/**
* Provides a stricter version of {@link Lazy} which throws an error when
* attempting to wrap `null`, `undefined`, or numeric or boolean values as a
* sequence.
*
* @public
* @returns {Function} A stricter version of the {@link 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
*/
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;
};
/**
* 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
* {@link 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 {@link Iterator} and
* {@link 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
* {@link #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.
*
* @public
* @constructor
*/
function Sequence() {}
/**
* Create a new constructor function for a type inheriting from `Sequence`.
*
* @public
* @param {string|Array.<string>} methodName 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 {@link #map}, {@link #filter}, etc.
* @param {Object} overrides 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
*/
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);
};
/**
* Gets the number of elements in the sequence. In some cases, this may
* require eagerly evaluating the sequence.
*
* @public
* @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
*/
Sequence.prototype.size = function size() {
return this.getIndex().length();
};
/**
* Creates an {@link 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.
*
* @public
* @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
*/
Sequence.prototype.getIterator = function getIterator() {
return new Iterator(this);
};
/**
* Gets the root sequence underlying the current chain of sequences.
*/
Sequence.prototype.root = function root() {
return this.parent.root();
};
/**
* Whether or not the current sequence is an asynchronous one. This is more
* accurate than checking `instanceof {@link AsyncSequence}` because, for
* example, `Lazy([1, 2, 3]).async().map(Lazy.identity)` returns a sequence
* that iterates asynchronously even though it's not an instance of
* `AsyncSequence`.
*/
Sequence.prototype.isAsync = function isAsync() {
return this.parent ? this.parent.isAsync() : false;
};
/**
* Evaluates the sequence and produces an appropriate value (an array in most
* cases, an object for {@link ObjectLikeSequence}s or a string for
* {@link StringLikeSequence}s).
*/
Sequence.prototype.value = function value() {
return this.toArray();
};
/**
* Applies the current transformation chain to a given source.
*
* @examples
* var sequence = Lazy([])
* .map(function(x) { return x * -1; })
* .filter(function(x) { return x % 2 === 0; });
*
* sequence.apply([1, 2, 3, 4]); // => [-2, -4]
*/
Sequence.prototype.apply = function apply(source) {
var root = this.root(),
previousSource = root.source,
result;
try {
root.source = source;
result = this.value();
} finally {
root.source = previousSource;
}
return result;
};
/**
* 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.
*
* @public
* @constructor
* @param {Sequence} sequence The sequence to iterate over.
*/
function Iterator(sequence) {
this.sequence = sequence;
this.index = -1;
}
/**
* Gets the current item this iterator is pointing to.
*
* @public
* @returns {*} The current item.
*/
Iterator.prototype.current = function current() {
return this.cachedIndex && this.cachedIndex.get(this.index);
};
/**
* Moves the iterator to the next item in a sequence, if possible.
*
* @public
* @returns {boolean} True if the iterator is able to move to a new item, or else
* false.
*/
Iterator.prototype.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;
};
/**
* 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.
*
* @public
* @returns {Array} An array containing the current contents of the sequence.
*
* @examples
* Lazy([1, 2, 3]).toArray() // => [1, 2, 3]
*/
Sequence.prototype.toArray = function toArray() {
return this.reduce(function(arr, element) {
arr.push(element);
return arr;
}, []);
};
/**
* Provides an indexed view into the sequence.
*
* For sequences that are already indexed, this will simply return the
* sequence. For non-indexed sequences, this will eagerly evaluate the
* sequence and cache the result (so subsequent calls will not create
* additional arrays).
*
* @returns {ArrayLikeSequence} A sequence containing the current contents of
* the sequence.
*
* @examples
* Lazy([1, 2, 3]).filter(isEven) // instanceof Lazy.Sequence
* Lazy([1, 2, 3]).filter(isEven).getIndex() // instanceof Lazy.ArrayLikeSequence
*/
Sequence.prototype.getIndex = function getIndex() {
if (!this.cachedIndex) {
this.cachedIndex = new ArrayWrapper(this.toArray());
}
return this.cachedIndex;
};
/**
* 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.
*
* @public
* @returns {ArrayLikeSequence} An indexed, memoized sequence containing this
* sequence's elements, cached after the first iteration.
*
* @example
* 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
*/
Sequence.prototype.memoize = function memoize() {
return new MemoizedSequence(this);
};
/**
* @constructor
*/
function MemoizedSequence(parent) {
this.parent = parent;
}
// MemoizedSequence needs to have its prototype set up after ArrayLikeSequence
/**
* Creates an object from a sequence of key/value pairs.
*
* @public
* @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 }
*/
Sequence.prototype.toObject = function toObject() {
return this.reduce(function(object, pair) {
object[pair[0]] = pair[1];
return object;
}, {});
};
/**
* Iterates over this sequence and executes a function for every element.
*
* @public
* @aka forEach
* @param {Function} fn The function to call on each element in the sequence.
* Return false from the function to end the iteration.
*
* @examples
* Lazy([1, 2, 3, 4]).each(fn) // calls fn 4 times
*/
Sequence.prototype.each = function each(fn) {
var iterator = this.getIterator(),
i = -1;
while (iterator.moveNext()) {
if (fn(iterator.current(), ++i) === false) {
return false;
}
}
return true;
};
Sequence.prototype.forEach = function forEach(fn) {
return this.each(fn);
};
/**
* Creates a new sequence whose values are calculated by passing this sequence's
* elements through some mapping function.
*
* @public
* @aka collect
* @param {Function} mapFn The mapping function used to project this sequence's
* elements onto a new sequence.
* @returns {Sequence} The new sequence.
*
* @examples
* Lazy([]).map(increment) // sequence: []
* Lazy([1, 2, 3]).map(increment) // sequence: [2, 3, 4]
*
* @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
*/
Sequence.prototype.map = function map(mapFn) {
return new MappedSequence(this, createCallback(mapFn));
};
Sequence.prototype.collect = function collect(mapFn) {
return this.map(mapFn);
};
/**
* @constructor
*/
function MappedSequence(parent, mapFn) {
this.parent = parent;
this.mapFn = mapFn;
}
MappedSequence.prototype = new Sequence();
MappedSequence.prototype.getIterator = function getIterator() {
return new MappingIterator(this.parent, this.mapFn);
};
MappedSequence.prototype.each = function each(fn) {
var mapFn = this.mapFn;
return this.parent.each(function(e, i) {
return fn(mapFn(e, i), i);
});
};
/**
* @constructor
*/
function MappingIterator(sequence, mapFn) {
this.iterator = sequence.getIterator();
this.mapFn = mapFn;
this.index = -1;
}
MappingIterator.prototype.current = function current() {
return this.mapFn(this.iterator.current(), this.index);
};
MappingIterator.prototype.moveNext = function moveNext() {
if (this.iterator.moveNext()) {
++this.index;
return true;
}
return false;
};
/**
* Creates a new sequence whose values are calculated by accessing the specified
* property from each element in this sequence.
*
* @public
* @param {string} propertyName 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"]
*/
Sequence.prototype.pluck = function pluck(property) {
return this.map(property);
};
/**
* Creates a new sequence whose values are calculated by invoking the specified
* function on each element in this sequence.
*
* @public
* @param {string} methodName 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"]
*/
Sequence.prototype.invoke = function invoke(methodName) {
return this.map(function(e) {
return e[methodName]();
});
};
/**
* Creates a new sequence whose values are the elements of this sequence which
* satisfy the specified predicate.
*
* @public
* @aka select
* @param {Function} filterFn 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
*/
Sequence.prototype.filter = function filter(filterFn) {
return new FilteredSequence(this, createCallback(filterFn));
};
Sequence.prototype.select = function select(filterFn) {
return this.filter(filterFn);
};
/**
* @constructor
*/
function FilteredSequence(parent, filterFn) {
this.parent = parent;
this.filterFn = filterFn;
}
FilteredSequence.prototype = new Sequence();
FilteredSequence.prototype.getIterator = function getIterator() {
return new FilteringIterator(this.parent, this.filterFn);
};
FilteredSequence.prototype.each = function each(fn) {
var filterFn = this.filterFn;
// I'm not proud of this, but it'll get the job done for now.
if (this.parent instanceof ObjectLikeSequence) {
return this.parent.each(function(v, k) {
if (filterFn(v, k)) {
return fn(v, k);
}
});
} else {
var j = 0;
return this.parent.each(function(e, i) {
if (filterFn(e, i)) {
return fn(e, j++);
}
});
}
};
FilteredSequence.prototype.reverse = function reverse() {
return this.parent.reverse().filter(this.filterFn);
};
/**
* @constructor
*/
function FilteringIterator(sequence, filterFn) {
this.iterator = sequence.getIterator();
this.filterFn = filterFn;
this.index = 0;
}
FilteringIterator.prototype.current = function current() {
return this.value;
};
FilteringIterator.prototype.moveNext = function moveNext() {
var iterator = this.iterator,
filterFn = this.filterFn,
value;
while (iterator.moveNext()) {
value = iterator.current();
if (filterFn(value, this.index++)) {
this.value = value;
return true;
}
}
this.value = undefined;
return false;
};
/**
* Creates a new sequence whose values exclude the elements of this sequence
* identified by the specified predicate.
*
* @public
* @param {Function} rejectFn 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 }]
*/
Sequence.prototype.reject = function reject(rejectFn) {
rejectFn = createCallback(rejectFn);
return this.filter(function(e) { return !rejectFn(e); });
};
/**
* Creates a new sequence whose values have the specified type, as determined
* by the `typeof` operator.
*
* @public
* @param {string} type 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: []
*/
Sequence.prototype.ofType = function ofType(type) {
return this.filter(function(e) { return typeof e === type; });
};
/**
* Creates a new sequence whose values are the elements of this sequence with
* property names and values matching those of the specified object.
*
* @public
* @param {Object} properties 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
*/
Sequence.prototype.where = function where(properties) {
return this.filter(properties);
};
/**
* 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.
*
* @public
* @returns {Sequence} The new sequence.
*
* @examples
* Lazy([1, 2, 3]).reverse() // sequence: [3, 2, 1]
* Lazy([]).reverse() // sequence: []
*/
Sequence.prototype.reverse = function reverse() {
return new ReversedSequence(this);
};
/**
* @constructor
*/
function ReversedSequence(parent) {
this.parent = parent;
}
ReversedSequence.prototype = new Sequence();
ReversedSequence.prototype.getIterator = function getIterator() {
return new ReversedIterator(this.parent);
};
/**
* @constuctor
*/
function ReversedIterator(sequence) {
this.sequence = sequence;
}
ReversedIterator.prototype.current = function current() {
return this.sequence.getIndex().get(this.index);
};
ReversedIterator.prototype.moveNext = function moveNext() {
var indexed = this.sequence.getIndex(),
length = indexed.length();
if (typeof this.index === "undefined") {
this.index = length;
}
return (--this.index >= 0);
};
/**
* Creates a new sequence with all of the elements of this one, plus those of
* the given array(s).
*
* @public
* @param {...*} 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]
*/
Sequence.prototype.concat = function concat(var_args) {
return new ConcatenatedSequence(this, arraySlice.call(arguments, 0));
};
/**
* @constructor
*/
function ConcatenatedSequence(parent, arrays) {
this.parent = parent;
this.arrays = arrays;
}
ConcatenatedSequence.prototype = new Sequence();
ConcatenatedSequence.prototype.each = function each(fn) {
var done = false,
i = 0;
this.parent.each(function(e) {
if (fn(e, i++) === false) {
done = true;
return false;
}
});
if (!done) {
Lazy(this.arrays).flatten().each(function(e) {
if (fn(e, i++) === false) {
return false;
}
});
}
};
/**
* 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.
*
* @public
* @aka head, take
* @param {number=} count 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]
*/
Sequence.prototype.first = function first(count) {
if (typeof count === "undefined") {
return getFirst(this);
}
return new TakeSequence(this, count);
};
Sequence.prototype.head =
Sequence.prototype.take = function (count) {
return this.first(count);
};
/**
* @constructor
*/
function TakeSequence(parent, count) {
this.parent = parent;
this.count = count;
}
TakeSequence.prototype = new Sequence();
TakeSequence.prototype.getIterator = function getIterator() {
return new TakeIterator(this.parent, this.count);
};
TakeSequence.prototype.each = function each(fn) {
var count = this.count,
i = 0;
var handle = this.parent.each(function(e) {
var result;
if (i < count) { result = fn(e, i++); }
if (i >= count) { return false; }
return result;
});
if (handle instanceof AsyncHandle) {
return handle;
}
return i === count;
};
/**
* @constructor
*/
function TakeIterator(sequence, count) {
this.iterator = sequence.getIterator();
this.count = count;
}
TakeIterator.prototype.current = function current() {
return this.iterator.current();
};
TakeIterator.prototype.moveNext = function moveNext() {
return ((--this.count >= 0) && this.iterator.moveNext());
};
/**
* 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.
*
* @public
* @param {Function} predicate
* @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: []
*/
Sequence.prototype.takeWhile = function takeWhile(predicate) {
return new TakeWhileSequence(this, predicate);
};
/**
* @constructor
*/
function TakeWhileSequence(parent, predicate) {
this.parent = parent;
this.predicate = predicate;
}
TakeWhileSequence.prototype = new Sequence();
TakeWhileSequence.prototype.each = function each(fn) {
var predicate = this.predicate,
finished = false,
j = 0;
var result = this.parent.each(function(e, i) {
if (!predicate(e, i)) {
finished = true;
return false;
}
return fn(e, j++);
});
if (result instanceof AsyncHandle) {
return result;
}
return finished;
};
/**
* Creates a new sequence comprising all but the last N elements of this
* sequence.
*
* @public
* @param {number=} count 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]
*/
Sequence.prototype.initial = function initial(count) {
if (typeof count === "undefined") {
count = 1;
}
return this.take(this.getIndex().length() - count);
};
/**
* 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.
*
* @public
* @param {number=} count 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]
*/
Sequence.prototype.last = function last(count) {
if (typeof count === "undefined") {
return this.reverse().first();
}
return this.reverse().take(count).reverse();
};
/**
* Returns the first element in this sequence with property names and values
* matching those of the specified object.
*
* @public
* @param {Object} properties 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
*/
Sequence.prototype.findWhere = function findWhere(properties) {
return this.where(properties).first();
};
/**
* Creates a new sequence comprising all but the first N elements of this
* sequence.
*
* @public
* @aka skip, tail, rest
* @param {number=} count 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: []
*/
Sequence.prototype.rest = function rest(count) {
return new DropSequence(this, count);
};
Sequence.prototype.skip =
Sequence.prototype.tail =
Sequence.prototype.drop = function drop(count) {
return this.rest(count);
};
/**
* @constructor
*/
function DropSequence(parent, count) {
this.parent = parent;
this.count = typeof count === "number" ? count : 1;
}
DropSequence.prototype = new Sequence();
DropSequence.prototype.each = function each(fn) {
var count = this.count,
dropped = 0,
i = 0;
return this.parent.each(function(e) {
if (dropped++ < count) { return; }
return fn(e, i++);
});
};
/**
* 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.
*
* @public
* @aka skipWhile
* @param {Function} predicate
* @returns {Sequence} The new sequence
*/
Sequence.prototype.dropWhile = function dropWhile(predicate) {
return new DropWhileSequence(this, predicate);
};
Sequence.prototype.skipWhile = function skipWhile(predicate) {
return this.dropWhile(predicate);
};
/**
* @constructor
*/
function DropWhileSequence(parent, predicate) {
this.parent = parent;
this.predicate = predicate;
}
DropWhileSequence.prototype = new Sequence();
DropWhileSequence.prototype.each = function each(fn) {
var predicate = this.predicate,
done = false;
return this.parent.each(function(e) {
if (!done) {
if (predicate(e)) {
return;
}
done = true;
}
return fn(e);
});
};
/**
* 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`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort),
* but obviously instead of modifying the collection it returns a new
* {@link Sequence} object.
*
* @public
* @param {Function=} sortFn 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
* @param {boolean} descending 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']
*/
Sequence.prototype.sort = function sort(sortFn, descending) {
sortFn || (sortFn = compare);
if (descending) { sortFn = reverseArguments(sortFn); }
return new SortedSequence(this, sortFn);
};
/**
* 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
*
* @public
* @param {Function} sortFn The function to call on the elements in this
* sequence, in order to sort them.
* @param {boolean} descending 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
*/
Sequence.prototype.sortBy = function sortBy(sortFn, descending) {
sortFn = createComparator(sortFn);
if (descending) { sortFn = reverseArguments(sortFn); }
return new SortedSequence(this, sortFn);
};
/**
* @constructor
*/
function SortedSequence(parent, sortFn) {
this.parent = parent;
this.sortFn = sortFn;
}
SortedSequence.prototype = new Sequence();
SortedSequence.prototype.each = function each(fn) {
var sortFn = this.sortFn,
result = this.parent.toArray();
result.sort(sortFn);
return forEach(result, fn);
};
/**
* @examples
* var items = [{ a: 4 }, { a: 3 }, { a: 5 }];
*
* Lazy(items).sortBy('a').reverse();
* // => sequence: [{ a: 5 }, { a: 4 }, { a: 3 }]
*
* Lazy(items).sortBy('a').reverse().reverse();
* // => sequence: [{ a: 3 }, { a: 4 }, { a: 5 }]
*/
SortedSequence.prototype.reverse = function reverse() {
return new SortedSequence(this.parent, reverseArguments(this.sortFn));
};
/**
* Creates a new {@link 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.
*
* @public
* @param {Function|string} keyFn 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.
* @returns {Sequence} The new sequence.
*
* @examples
* function oddOrEven(x) {
* return x % 2 === 0 ? 'even' : 'odd';
* }
*
* 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([
* { name: 'toString' },
* { name: 'toString' }
* ]).groupBy('name');
* // => sequence: {
* 'toString': [
* { name: 'toString' },
* { name: 'toString' }
* ]
* }
*/
Sequence.prototype.groupBy = function groupBy(keyFn) {
return new GroupedSequence(this, keyFn);
};
/**
* @constructor
*/
function GroupedSequence(parent, keyFn) {
this.parent = parent;
this.keyFn = keyFn;
}
// GroupedSequence must have its prototype set after ObjectLikeSequence has
// been fully initialized.
/**
* Creates a new {@link ObjectLikeSequence} comprising the elements in this
* one, indexed according to some key.
*
* @public
* @param {Function|string} keyFn 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.
* @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 }
*/
Sequence.prototype.indexBy = function(keyFn) {
return new IndexedSequence(this, keyFn);
};
/**
* @constructor
*/
function IndexedSequence(parent, keyFn) {
this.parent = parent;
this.keyFn = keyFn;
}
// IndexedSequence must have its prototype set after ObjectLikeSequence has
// been fully initialized.
/**
* Creates a new {@link 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.
*
* @public
* @param {Function|string} keyFn 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
*/
Sequence.prototype.countBy = function countBy(keyFn) {
return new CountedSequence(this, keyFn);
};
/**
* @constructor
*/
function CountedSequence(parent, keyFn) {
this.parent = parent;
this.keyFn = keyFn;
}
// CountedSequence, like GroupedSequence, must have its prototype set after
// ObjectLikeSequence has been fully initialized.
/**
* Creates a new sequence with every unique element from this one appearing
* exactly once (i.e., with duplicates removed).
*
* @public
* @aka unique
* @returns {Sequence} The new sequence.
*
* @examples
* Lazy([1, 2, 2, 3, 3, 3]).uniq() // sequence: [1, 2, 3]
*
* @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
*/
Sequence.prototype.uniq = function uniq(keyFn) {
return new UniqueSequence(this, keyFn);
};
Sequence.prototype.unique = function unique(keyFn) {
return this.uniq(keyFn);
};
/**
* @constructor
*/
function UniqueSequence(parent, keyFn) {
this.parent = parent;
this.keyFn = keyFn;
}
UniqueSequence.prototype = new Sequence();
UniqueSequence.prototype.each = function each(fn) {
var cache = new Set(),
keyFn = this.keyFn,
i = 0;
if (keyFn) {
keyFn = createCallback(keyFn);
return this.parent.each(function(e) {
if (cache.add(keyFn(e))) {
return fn(e, i++);
}
});
} else {
return this.parent.each(function(e) {
if (cache.add(e)) {
return fn(e, i++);
}
});
}
};
/**
* Creates a new sequence by combining the elements from this sequence with
* corresponding elements from the specified array(s).
*
* @public
* @param {...Array} var_args 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
*/
Sequence.prototype.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));
}
};
/**
* @constructor
*/
function ZippedSequence(parent, arrays) {
this.parent = parent;
this.arrays = arrays;
}
ZippedSequence.prototype = new Sequence();
ZippedSequence.prototype.each = function each(fn) {
var arrays = this.arrays,
i = 0;
this.parent.each(function(e) {
var group = [e];
for (var j = 0; j < arrays.length; ++j) {
if (arrays[j].length > i) {
group.push(arrays[j][i]);
}
}
return fn(group, i++);
});
};
/**
* Creates a new sequence with the same elements as this one, in a randomized
* order.
*
* @public
* @returns {Sequence} The new sequence.
*
* @examples
* Lazy([1, 2, 3, 4, 5]).shuffle().value() // =~ [1, 2, 3, 4, 5]
*/
Sequence.prototype.shuffle = function shuffle() {
return new ShuffledSequence(this);
};
/**
* @constructor
*/
function ShuffledSequence(parent) {
this.parent = parent;
}
ShuffledSequence.prototype = new Sequence();
ShuffledSequence.prototype.each = function each(fn) {
var shuffled = this.parent.toArray(),
floor = Math.floor,
random = Math.random,
j = 0;
for (var i = shuffled.length - 1; i > 0; --i) {
swap(shuffled, i, floor(random() * i) + 1);
if (fn(shuffled[i], j++) === false) {
return;
}
}
fn(shuffled[0], j);
};
/**
* 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.
*
* @public
* @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]
*/
Sequence.prototype.flatten = function flatten() {
return new FlattenedSequence(this);
};
/**
* @constructor
*/
function FlattenedSequence(parent) {
this.parent = parent;
}
FlattenedSequence.prototype = new Sequence();
FlattenedSequence.prototype.each = function each(fn) {
var index = 0;
return this.parent.each(function recurseVisitor(e) {
if (e instanceof Array) {
return forEach(e, recurseVisitor);
}
if (e instanceof Sequence) {
return e.each(recurseVisitor);
}
return fn(e, index++);
});
};
/**
* Creates a new sequence with the same elements as this one, except for all
* falsy values (`false`, `0`, `""`, `null`, and `undefined`).
*
* @public
* @returns {Sequence} The new sequence.
*
* @examples
* Lazy(["foo", null, "bar", undefined]).compact() // sequence: ["foo", "bar"]
*/
Sequence.prototype.compact = function compact() {
return this.filter(function(e) { return !!e; });
};
/**
* Creates a new sequence with all the elements of this sequence that are not
* also among the specified arguments.
*
* @public
* @aka difference
* @param {...*} 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]
*/
Sequence.prototype.without = function without(var_args) {
return new WithoutSequence(this, arraySlice.call(arguments, 0));
};
Sequence.prototype.difference = function difference(var_args) {
return this.without.apply(this, arguments);
};
/**
* @constructor
*/
function WithoutSequence(parent, values) {
this.parent = parent;
this.values = values;
}
WithoutSequence.prototype = new Sequence();
WithoutSequence.prototype.each = function each(fn) {
var set = createSet(this.values),
i = 0;
return this.parent.each(function(e) {
if (!set.contains(e)) {
return fn(e, i++);
}
});
};
/**
* Creates a new sequence with all the unique elements either in this sequence
* or among the specified arguments.
*
* @public
* @param {...*} 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"]
*/
Sequence.prototype.union = function union(var_args) {
return this.concat(var_args).uniq();
};
/**
* Creates a new sequence with all the elements of this sequence that also
* appear among the specified arguments.
*
* @public
* @param {...*} 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"]
*/
Sequence.prototype.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));
}
};
/**
* @constructor
*/
function IntersectionSequence(parent, arrays) {
this.parent = parent;
this.arrays = arrays;
}
IntersectionSequence.prototype = new Sequence();
IntersectionSequence.prototype.each = function each(fn) {
var sets = Lazy(this.arrays).map(function(values) {
return new UniqueMemoizer(Lazy(values).getIterator());
});
var setIterator = new UniqueMemoizer(sets.getIterator()),
i = 0;
return this.parent.each(function(e) {
var includedInAll = true;
setIterator.each(function(set) {
if (!set.contains(e)) {
includedInAll = false;
return false;
}
});
if (includedInAll) {
return fn(e, i++);
}
});
};
/**
* @constructor
*/
function UniqueMemoizer(iterator) {
this.iterator = iterator;
this.set = new Set();
this.memo = [];
this.currentValue = undefined;
}
UniqueMemoizer.prototype.current = function current() {
return this.currentValue;
};
UniqueMemoizer.prototype.moveNext = function moveNext() {
var iterator = this.iterator,
set = this.set,
memo = this.memo,
current;
while (iterator.moveNext()) {
current = iterator.current();
if (set.add(current)) {
memo.push(current);
this.currentValue = current;
return true;
}
}
return false;
};
UniqueMemoizer.prototype.each = function each(fn) {
var memo = this.memo,
length = memo.length,
i = -1;
while (++i < length) {
if (fn(memo[i], i) === false) {
return false;
}
}
while (this.moveNext()) {
if (fn(this.currentValue, i++) === false) {
break;
}
}
};
UniqueMemoizer.prototype.contains = function contains(e) {
if (this.set.contains(e)) {
return true;
}
while (this.moveNext()) {
if (this.currentValue === e) {
return true;
}
}
return false;
};
/**
* Checks whether every element in this sequence satisfies a given predicate.
*
* @public
* @aka all
* @param {Function} predicate 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
*/
Sequence.prototype.every = function every(predicate) {
predicate = createCallback(predicate);
return this.each(function(e, i) {
return !!predicate(e, i);
});
};
Sequence.prototype.all = function all(predicate) {
return this.every(predicate);
};
/**
* 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).
*
* @public
* @aka any
* @param {Function=} predicate 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
*/
Sequence.prototype.some = function some(predicate) {
predicate = createCallback(predicate, true);
var success = false;
this.each(function(e) {
if (predicate(e)) {
success = true;
return false;
}
});
return success;
};
Sequence.prototype.any = function any(predicate) {
return this.some(predicate);
};
/**
* Checks whether NO elements in this sequence satisfy the given predicate
* (the opposite of {@link Sequence#all}, basically).
*
* @public
* @param {Function=} predicate 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
*/
Sequence.prototype.none = function none(predicate) {
return !this.any(predicate);
};
/**
* Checks whether the sequence has no elements.
*
* @public
* @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
*/
Sequence.prototype.isEmpty = function isEmpty() {
return !this.any();
};
/**
* Performs (at worst) a linear search from the head of this sequence,
* returning the first index at which the specified value is found.
*
* @public
* @param {*} 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
*/
Sequence.prototype.indexOf = function indexOf(value) {
var foundIndex = -1;
this.each(function(e, i) {
if (e === value) {
foundIndex = i;
return false;
}
});
return foundIndex;
};
/**
* Performs (at worst) a linear search from the tail of this sequence,
* returning the last index at which the specified value is found.
*
* @public
* @param {*} 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
*/
Sequence.prototype.lastIndexOf = function lastIndexOf(value) {
var index = this.reverse().indexOf(value);
if (index !== -1) {
index = this.getIndex().length() - index - 1;
}
return index;
};
/**
* 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.
*
* @public
* @param {*} 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
*/
Sequence.prototype.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;
};
/**
* Checks whether the given value is in this sequence.
*
* @public
* @param {*} 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
*/
Sequence.prototype.contains = function contains(value) {
return this.indexOf(value) !== -1;
};
/**
* 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 {@link AsyncHandle}
* whose `onComplete` method can be called to supply a callback to handle the
* final result once iteration has completed.
*
* @public
* @aka inject, foldl
* @param {Function} aggregator 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.
* @param {*=} 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 {@link 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
*/
Sequence.prototype.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;
};
Sequence.prototype.inject =
Sequence.prototype.foldl = function foldl(aggregator, memo) {
return this.reduce(aggregator, memo);
};
/**
* Aggregates a sequence, from the tail, into a single value according to some
* accumulator function.
*
* @public
* @aka foldr
* @param {Function} aggregator 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.
* @param {*} 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"
*/
Sequence.prototype.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 i = this.getIndex().length() - 1;
return this.reverse().reduce(function(m, e) {
return aggregator(m, e, i--);
}, memo);
};
Sequence.prototype.foldr = function foldr(aggregator, memo) {
return this.reduceRight(aggregator, memo);
};
/**
* 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.
*
* @public
* @param {number} length 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]]
*/
Sequence.prototype.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();
};
/**
* Breaks this sequence into chunks (arrays) of a specified length.
*
* @public
* @param {number} size 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
*/
Sequence.prototype.chunk = function chunk(size) {
if (size < 1) {
throw new Error("You must specify a positive chunk size.");
}
return new ChunkedSequence(this, size);
};
/**
* @constructor
*/
function ChunkedSequence(parent, size) {
this.parent = parent;
this.chunkSize = size;
}
ChunkedSequence.prototype = new Sequence();
ChunkedSequence.prototype.getIterator = function getIterator() {
return new ChunkedIterator(this.parent, this.chunkSize);
};
/**
* @constructor
*/
function ChunkedIterator(sequence, size) {
this.iterator = sequence.getIterator();
this.size = size;
}
ChunkedIterator.prototype.current = function current() {
return this.currentChunk;
};
ChunkedIterator.prototype.moveNext = function moveNext() {
var iterator = this.iterator,
chunkSize = this.size,
chunk = [];
while (chunk.length < chunkSize && iterator.moveNext()) {
chunk.push(iterator.current());
}
if (chunk.length === 0) {
return false;
}
this.currentChunk = chunk;
return true;
};
/**
* Passes each element in the sequence to the specified callback during
* iteration. This is like {@link 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.
*
* @public
* @param {Function} callback 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
*/
Sequence.prototype.tap = function tap(callback) {
return new TappedSequence(this, callback);
};
/**
* @constructor
*/
function TappedSequence(parent, callback) {
this.parent = parent;
this.callback = callback;
}
TappedSequence.prototype = new Sequence();
TappedSequence.prototype.each = function each(fn) {
var callback = this.callback;
return this.parent.each(function(e, i) {
callback(e, i);
return fn(e, i);
});
};
/**
* Seaches for the first element in the sequence satisfying a given predicate.
*
* @public
* @aka detect
* @param {Function} predicate 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
*/
Sequence.prototype.find = function find(predicate) {
return this.filter(predicate).first();
};
Sequence.prototype.detect = function detect(predicate) {
return this.find(predicate);
};
/**
* Gets the minimum value in the sequence.
*
* @public
* @param {Function=} valueFn 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
*/
Sequence.prototype.min = function min(valueFn) {
if (typeof valueFn !== "undefined") {
return this.minBy(valueFn);
}
return this.reduce(function(x, y) { return y < x ? y : x; }, Infinity);
};
Sequence.prototype.minBy = function minBy(valueFn) {
valueFn = createCallback(valueFn);
return this.reduce(function(x, y) { return valueFn(y) < valueFn(x) ? y : x; });
};
/**
* Gets the maximum value in the sequence.
*
* @public
* @param {Function=} valueFn 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
*/
Sequence.prototype.max = function max(valueFn) {
if (typeof valueFn !== "undefined") {
return this.maxBy(valueFn);
}
return this.reduce(function(x, y) { return y > x ? y : x; }, -Infinity);
};
Sequence.prototype.maxBy = function maxBy(valueFn) {
valueFn = createCallback(valueFn);
return this.reduce(function(x, y) { return valueFn(y) > valueFn(x) ? y : x; });
};
/**
* Gets the sum of the values in the sequence.
*
* @public
* @param {Function=} valueFn 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
*/
Sequence.prototype.sum = function sum(valueFn) {
if (typeof valueFn !== "undefined") {
return this.sumBy(valueFn);
}
return this.reduce(function(x, y) { return x + y; }, 0);
};
Sequence.prototype.sumBy = function sumBy(valueFn) {
valueFn = createCallback(valueFn);
return this.reduce(function(x, y) { return x + valueFn(y); }, 0);
};
/**
* Creates a string from joining together all of the elements in this sequence,
* separated by the given delimiter.
*
* @public
* @aka toString
* @param {string=} delimiter 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"
*/
Sequence.prototype.join = function join(delimiter) {
delimiter = typeof delimiter === "string" ? delimiter : ",";
return this.reduce(function(str, e) {
if (str.length > 0) {
str += delimiter;
}
return str + e;
}, "");
};
Sequence.prototype.toString = function toString(delimiter) {
return this.join(delimiter);
};
/**
* Creates a sequence, with the same elements as this one, that will be iterated
* over asynchronously when calling `each`.
*
* @public
* @param {number=} interval 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
*/
Sequence.prototype.async = function async(interval) {
return new AsyncSequence(this, interval);
};
/**
* @constructor
*/
function SimpleIntersectionSequence(parent, array) {
this.parent = parent;
this.array = array;
this.each = getEachForIntersection(array);
}
SimpleIntersectionSequence.prototype = new Sequence();
SimpleIntersectionSequence.prototype.eachMemoizerCache = function eachMemoizerCache(fn) {
var iterator = new UniqueMemoizer(Lazy(this.array).getIterator()),
i = 0;
return this.parent.each(function(e) {
if (iterator.contains(e)) {
return fn(e, i++);
}
});
};
SimpleIntersectionSequence.prototype.eachArrayCache = function eachArrayCache(fn) {
var array = this.array,
find = arrayContains,
i = 0;
return this.parent.each(function(e) {
if (find(array, e)) {
return fn(e, i++);
}
});
};
function getEachForIntersection(source) {
if (source.length < 40) {
return SimpleIntersectionSequence.prototype.eachArrayCache;
} else {
return SimpleIntersectionSequence.prototype.eachMemoizerCache;
}
}
/**
* An optimized version of {@link ZippedSequence}, when zipping a sequence with
* only one array.
*
* @param {Sequence} parent The underlying sequence.
* @param {Array} array The array with which to zip the sequence.
* @constructor
*/
function SimpleZippedSequence(parent, array) {
this.parent = parent;
this.array = array;
}
SimpleZippedSequence.prototype = new Sequence();
SimpleZippedSequence.prototype.each = function each(fn) {
var array = this.array;
return this.parent.each(function(e, i) {
return fn([e, array[i]], i);
});
};
/**
* An `ArrayLikeSequence` is a {@link Sequence} that provides random access to
* its elements. This extends the API for iterating with the additional methods
* {@link #get} and {@link #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:
*
* - {@link Sequence#map}
* - {@link ArrayLikeSequence#slice}
* - {@link Sequence#take} and {@link Sequence#drop}
* - {@link Sequence#reverse}
*
* 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
* {@link 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:
*
* - {@link Sequence#filter}
* - {@link Sequence#uniq}
* - {@link Sequence#union}
* - {@link Sequence#intersect}
*
* etc. The above methods only return ordinary {@link Sequence} objects.
*
* Defining custom array-like sequences
* ------------------------------------
*
* Creating a custom `ArrayLikeSequence` is essentially the same as creating a
* custom {@link 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 {@link #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 {@link Sequence}
* types, this example really could have been implemented using a function
* already available as part of Lazy.js: in this case, {@link Sequence#map}.)
*
* @public
* @constructor
*
* @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
*/
function ArrayLikeSequence() {}
ArrayLikeSequence.prototype = new Sequence();
/**
* Create a new constructor function for a type inheriting from
* `ArrayLikeSequence`.
*
* @public
* @param {string|Array.<string>} methodName 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.
* @param {Object} overrides 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]
*/
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);
};
/**
* Returns the element at the specified index.
*
* @public
* @param {number} i 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
*/
ArrayLikeSequence.prototype.get = function get(i) {
return this.parent.get(i);
};
/**
* Returns the length of the sequence.
*
* @public
* @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
*/
ArrayLikeSequence.prototype.length = function length() {
return this.parent.length();
};
/**
* Returns the current sequence (since it is already indexed).
*/
ArrayLikeSequence.prototype.getIndex = function getIndex() {
return this;
};
/**
* An optimized version of {@link Sequence#getIterator}.
*/
ArrayLikeSequence.prototype.getIterator = function getIterator() {
return new IndexedIterator(this);
};
/**
* An optimized version of {@link Iterator} meant to work with already-indexed
* sequences.
*
* @param {ArrayLikeSequence} sequence The sequence to iterate over.
* @constructor
*/
function IndexedIterator(sequence) {
this.sequence = sequence;
this.index = -1;
}
IndexedIterator.prototype.current = function current() {
return this.sequence.get(this.index);
};
IndexedIterator.prototype.moveNext = function moveNext() {
if (this.index >= this.sequence.length() - 1) {
return false;
}
++this.index;
return true;
};
/**
* An optimized version of {@link Sequence#each}.
*/
ArrayLikeSequence.prototype.each = function each(fn) {
var length = this.length(),
i = -1;
while (++i < length) {
if (fn(this.get(i), i) === false) {
return false;
}
}
return true;
};
/**
* Returns a new sequence with the same elements as this one, minus the last
* element.
*
* @public
* @returns {ArrayLikeSequence} The new array-like sequence.
*
* @examples
* Lazy([1, 2, 3]).pop() // sequence: [1, 2]
* Lazy([]).pop() // sequence: []
*/
ArrayLikeSequence.prototype.pop = function pop() {
return this.initial();
};
/**
* Returns a new sequence with the same elements as this one, minus the first
* element.
*
* @public
* @returns {ArrayLikeSequence} The new array-like sequence.
*
* @examples
* Lazy([1, 2, 3]).shift() // sequence: [2, 3]
* Lazy([]).shift() // sequence: []
*/
ArrayLikeSequence.prototype.shift = function shift() {
return this.drop();
};
/**
* 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.
*
* @public
* @param {number} begin The index at which the new sequence should start.
* @param {number=} end 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]
*/
ArrayLikeSequence.prototype.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;
};
/**
* An optimized version of {@link Sequence#map}, which creates an
* {@link ArrayLikeSequence} so that the result still provides random access.
*
* @public
*
* @examples
* Lazy([1, 2, 3]).map(Lazy.identity) // instanceof Lazy.ArrayLikeSequence
*/
ArrayLikeSequence.prototype.map = function map(mapFn) {
return new IndexedMappedSequence(this, createCallback(mapFn));
};
/**
* @constructor
*/
function IndexedMappedSequence(parent, mapFn) {
this.parent = parent;
this.mapFn = mapFn;
}
IndexedMappedSequence.prototype = new ArrayLikeSequence();
IndexedMappedSequence.prototype.get = function get(i) {
if (i < 0 || i >= this.parent.length()) {
return undefined;
}
return this.mapFn(this.parent.get(i), i);
};
/**
* An optimized version of {@link Sequence#filter}.
*/
ArrayLikeSequence.prototype.filter = function filter(filterFn) {
return new IndexedFilteredSequence(this, createCallback(filterFn));
};
/**
* @constructor
*/
function IndexedFilteredSequence(parent, filterFn) {
this.parent = parent;
this.filterFn = filterFn;
}
IndexedFilteredSequence.prototype = new FilteredSequence();
IndexedFilteredSequence.prototype.each = function each(fn) {
var parent = this.parent,
filterFn = this.filterFn,
length = this.parent.length(),
i = -1,
j = 0,
e;
while (++i < length) {
e = parent.get(i);
if (filterFn(e, i) && fn(e, j++) === false) {
return false;
}
}
return true;
};
/**
* An optimized version of {@link Sequence#reverse}, which creates an
* {@link ArrayLikeSequence} so that the result still provides random access.
*
* @public
*
* @examples
* Lazy([1, 2, 3]).reverse() // instanceof Lazy.ArrayLikeSequence
*/
ArrayLikeSequence.prototype.reverse = function reverse() {
return new IndexedReversedSequence(this);
};
/**
* @constructor
*/
function IndexedReversedSequence(parent) {
this.parent = parent;
}
IndexedReversedSequence.prototype = new ArrayLikeSequence();
IndexedReversedSequence.prototype.get = function get(i) {
return this.parent.get(this.length() - i - 1);
};
/**
* An optimized version of {@link Sequence#first}, which creates an
* {@link ArrayLikeSequence} so that the result still provides random access.
*
* @public
*
* @examples
* Lazy([1, 2, 3]).first(2) // instanceof Lazy.ArrayLikeSequence
*/
ArrayLikeSequence.prototype.first = function first(count) {
if (typeof count === "undefined") {
return this.get(0);
}
return new IndexedTakeSequence(this, count);
};
/**
* @constructor
*/
function IndexedTakeSequence(parent, count) {
this.parent = parent;
this.count = count;
}
IndexedTakeSequence.prototype = new ArrayLikeSequence();
IndexedTakeSequence.prototype.length = function length() {
var parentLength = this.parent.length();
return this.count <= parentLength ? this.count : parentLength;
};
/**
* An optimized version of {@link Sequence#rest}, which creates an
* {@link ArrayLikeSequence} so that the result still provides random access.
*
* @public
*
* @examples
* Lazy([1, 2, 3]).rest() // instanceof Lazy.ArrayLikeSequence
*/
ArrayLikeSequence.prototype.rest = function rest(count) {
return new IndexedDropSequence(this, count);
};
/**
* @constructor
*/
function IndexedDropSequence(parent, count) {
this.parent = parent;
this.count = typeof count === "number" ? count : 1;
}
IndexedDropSequence.prototype = new ArrayLikeSequence();
IndexedDropSequence.prototype.get = function get(i) {
return this.parent.get(this.count + i);
};
IndexedDropSequence.prototype.length = function length() {
var parentLength = this.parent.length();
return this.count <= parentLength ? parentLength - this.count : 0;
};
/**
* An optimized version of {@link Sequence#concat} that returns another
* {@link ArrayLikeSequence} *if* the argument is an array.
*
* @public
* @param {...*} var_args
*
* @examples
* Lazy([1, 2]).concat([3, 4]) // instanceof Lazy.ArrayLikeSequence
* Lazy([1, 2]).concat([3, 4]) // sequence: [1, 2, 3, 4]
*/
ArrayLikeSequence.prototype.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);
}
};
/**
* @constructor
*/
function IndexedConcatenatedSequence(parent, other) {
this.parent = parent;
this.other = other;
}
IndexedConcatenatedSequence.prototype = new ArrayLikeSequence();
IndexedConcatenatedSequence.prototype.get = function get(i) {
var parentLength = this.parent.length();
if (i < parentLength) {
return this.parent.get(i);
} else {
return this.other[i - parentLength];
}
};
IndexedConcatenatedSequence.prototype.length = function length() {
return this.parent.length() + this.other.length;
};
/**
* An optimized version of {@link Sequence#uniq}.
*/
ArrayLikeSequence.prototype.uniq = function uniq(keyFn) {
return new IndexedUniqueSequence(this, createCallback(keyFn));
};
/**
* @param {ArrayLikeSequence} parent
* @constructor
*/
function IndexedUniqueSequence(parent, keyFn) {
this.parent = parent;
this.each = getEachForParent(parent);
this.keyFn = keyFn;
}
IndexedUniqueSequence.prototype = new Sequence();
IndexedUniqueSequence.prototype.eachArrayCache = function eachArrayCache(fn) {
// Basically the same implementation as w/ the set, but using an array because
// it's cheaper for smaller sequences.
var parent = this.parent,
keyFn = this.keyFn,
length = parent.length(),
cache = [],
find = arrayContains,
key, value,
i = -1,
j = 0;
while (++i < length) {
value = parent.get(i);
key = keyFn(value);
if (!find(cache, key)) {
cache.push(key);
if (fn(value, j++) === false) {
return false;
}
}
}
};
IndexedUniqueSequence.prototype.eachSetCache = UniqueSequence.prototype.each;
function getEachForParent(parent) {
if (parent.length() < 100) {
return IndexedUniqueSequence.prototype.eachArrayCache;
} else {
return UniqueSequence.prototype.each;
}
}
// Now that we've fully initialized the ArrayLikeSequence prototype, we can
// set the prototype for MemoizedSequence.
MemoizedSequence.prototype = new ArrayLikeSequence();
MemoizedSequence.prototype.cache = function cache() {
return this.cachedResult || (this.cachedResult = this.parent.toArray());
};
MemoizedSequence.prototype.get = function get(i) {
return this.cache()[i];
};
MemoizedSequence.prototype.length = function length() {
return this.cache().length;
};
MemoizedSequence.prototype.slice = function slice(begin, end) {
return this.cache().slice(begin, end);
};
MemoizedSequence.prototype.toArray = function toArray() {
return this.cache().slice(0);
};
/**
* ArrayWrapper is the most basic {@link Sequence}. It directly wraps an array
* and implements the same methods as {@link ArrayLikeSequence}, but more
* efficiently.
*
* @constructor
*/
function ArrayWrapper(source) {
this.source = source;
}
ArrayWrapper.prototype = new ArrayLikeSequence();
ArrayWrapper.prototype.root = function root() {
return this;
};
ArrayWrapper.prototype.isAsync = function isAsync() {
return false;
};
/**
* Returns the element at the specified index in the source array.
*
* @param {number} i The index to access.
* @returns {*} The element.
*/
ArrayWrapper.prototype.get = function get(i) {
return this.source[i];
};
/**
* Returns the length of the source array.
*
* @returns {number} The length.
*/
ArrayWrapper.prototype.length = function length() {
return this.source.length;
};
/**
* An optimized version of {@link Sequence#each}.
*/
ArrayWrapper.prototype.each = function each(fn) {
return forEach(this.source, fn);
};
/**
* An optimized version of {@link Sequence#map}.
*/
ArrayWrapper.prototype.map = function map(mapFn) {
return new MappedArrayWrapper(this, createCallback(mapFn));
};
/**
* An optimized version of {@link Sequence#filter}.
*/
ArrayWrapper.prototype.filter = function filter(filterFn) {
return new FilteredArrayWrapper(this, createCallback(filterFn));
};
/**
* An optimized version of {@link Sequence#uniq}.
*/
ArrayWrapper.prototype.uniq = function uniq(keyFn) {
return new UniqueArrayWrapper(this, keyFn);
};
/**
* An optimized version of {@link ArrayLikeSequence#concat}.
*
* @param {...*} var_args
*/
ArrayWrapper.prototype.concat = function concat(var_args) {
if (arguments.length === 1 && arguments[0] instanceof Array) {
return new ConcatArrayWrapper(this, (/** @type {Array} */ var_args));
} else {
return ArrayLikeSequence.prototype.concat.apply(this, arguments);
}
};
/**
* An optimized version of {@link Sequence#toArray}.
*/
ArrayWrapper.prototype.toArray = function toArray() {
return this.source.slice(0);
};
/**
* @constructor
*/
function MappedArrayWrapper(parent, mapFn) {
this.parent = parent;
this.mapFn = mapFn;
}
MappedArrayWrapper.prototype = new ArrayLikeSequence();
MappedArrayWrapper.prototype.get = function get(i) {
var source = this.parent.source;
if (i < 0 || i >= source.length) {
return undefined;
}
return this.mapFn(source[i]);
};
MappedArrayWrapper.prototype.length = function length() {
return this.parent.source.length;
};
MappedArrayWrapper.prototype.each = function each(fn) {
var source = this.parent.source,
length = source.length,
mapFn = this.mapFn,
i = -1;
while (++i < length) {
if (fn(mapFn(source[i], i), i) === false) {
return false;
}
}
return true;
};
/**
* @constructor
*/
function FilteredArrayWrapper(parent, filterFn) {
this.parent = parent;
this.filterFn = filterFn;
}
FilteredArrayWrapper.prototype = new FilteredSequence();
FilteredArrayWrapper.prototype.each = function each(fn) {
var source = this.parent.source,
filterFn = this.filterFn,
length = source.length,
i = -1,
j = 0,
e;
while (++i < length) {
e = source[i];
if (filterFn(e, i) && fn(e, j++) === false) {
return false;
}
}
return true;
};
/**
* @constructor
*/
function UniqueArrayWrapper(parent, keyFn) {
this.parent = parent;
this.each = getEachForSource(parent.source);
this.keyFn = keyFn;
}
UniqueArrayWrapper.prototype = new Sequence();
UniqueArrayWrapper.prototype.eachNoCache = function eachNoCache(fn) {
var source = this.parent.source,
keyFn = this.keyFn,
length = source.length,
find = arrayContainsBefore,
value,
// Yes, this is hideous.
// Trying to get performance first, will refactor next!
i = -1,
k = 0;
while (++i < length) {
value = source[i];
if (!find(source, value, i, keyFn) && fn(value, k++) === false) {
return false;
}
}
return true;
};
UniqueArrayWrapper.prototype.eachArrayCache = function eachArrayCache(fn) {
// Basically the same implementation as w/ the set, but using an array because
// it's cheaper for smaller sequences.
var source = this.parent.source,
keyFn = this.keyFn,
length = source.length,
cache = [],
find = arrayContains,
key, value,
i = -1,
j = 0;
if (keyFn) {
keyFn = createCallback(keyFn);
while (++i < length) {
value = source[i];
key = keyFn(value);
if (!find(cache, key)) {
cache.push(key);
if (fn(value, j++) === false) {
return false;
}
}
}
} else {
while (++i < length) {
value = source[i];
if (!find(cache, value)) {
cache.push(value);
if (fn(value, j++) === false) {
return false;
}
}
}
}
return true;
};
UniqueArrayWrapper.prototype.eachSetCache = UniqueSequence.prototype.each;
/**
* My latest findings here...
*
* So I hadn't really given the set-based approach enough credit. The main issue
* was that my Set implementation was totally not optimized at all. After pretty
* heavily optimizing it (just take a look; it's a monstrosity now!), it now
* becomes the fastest option for much smaller values of N.
*/
function getEachForSource(source) {
if (source.length < 40) {
return UniqueArrayWrapper.prototype.eachNoCache;
} else if (source.length < 100) {
return UniqueArrayWrapper.prototype.eachArrayCache;
} else {
return UniqueArrayWrapper.prototype.eachSetCache;
}
}
/**
* @constructor
*/
function ConcatArrayWrapper(parent, other) {
this.parent = parent;
this.other = other;
}
ConcatArrayWrapper.prototype = new ArrayLikeSequence();
ConcatArrayWrapper.prototype.get = function get(i) {
var source = this.parent.source,
sourceLength = source.length;
if (i < sourceLength) {
return source[i];
} else {
return this.other[i - sourceLength];
}
};
ConcatArrayWrapper.prototype.length = function length() {
return this.parent.source.length + this.other.length;
};
ConcatArrayWrapper.prototype.each = function each(fn) {
var source = this.parent.source,
sourceLength = source.length,
other = this.other,
otherLength = other.length,
i = 0,
j = -1;
while (++j < sourceLength) {
if (fn(source[j], i++) === false) {
return false;
}
}
j = -1;
while (++j < otherLength) {
if (fn(other[j], i++) === false) {
return false;
}
}
return true;
};
/**
* 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`.
*
* @public
* @constructor
*
* @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
*/
function ObjectLikeSequence() {}
ObjectLikeSequence.prototype = new Sequence();
/**
* Create a new constructor function for a type inheriting from
* `ObjectLikeSequence`.
*
* @public
* @param {string|Array.<string>} methodName 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.
* @param {Object} overrides 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'
*/
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);
};
ObjectLikeSequence.prototype.value = function value() {
return this.toObject();
};
/**
* Gets the element at the specified key in this sequence.
*
* @public
* @param {string} key 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
*/
ObjectLikeSequence.prototype.get = function get(key) {
var pair = this.pairs().find(function(pair) {
return pair[0] === key;
});
return pair ? pair[1] : undefined;
};
/**
* Returns a {@link Sequence} whose elements are the keys of this object-like
* sequence.
*
* @public
* @returns {Sequence} The sequence based on this sequence's keys.
*
* @examples
* Lazy({ hello: "hola", goodbye: "hasta luego" }).keys() // sequence: ["hello", "goodbye"]
*/
ObjectLikeSequence.prototype.keys = function keys() {
return this.map(function(v, k) { return k; });
};
/**
* Returns a {@link Sequence} whose elements are the values of this object-like
* sequence.
*
* @public
* @returns {Sequence} The sequence based on this sequence's values.
*
* @examples
* Lazy({ hello: "hola", goodbye: "hasta luego" }).values() // sequence: ["hola", "hasta luego"]
*/
ObjectLikeSequence.prototype.values = function values() {
return this.map(function(v, k) { return v; });
};
/**
* Throws an exception. Asynchronous iteration over object-like sequences is
* not supported.
*
* @public
* @examples
* Lazy({ foo: 'bar' }).async() // throws
*/
ObjectLikeSequence.prototype.async = function async() {
throw new Error('An ObjectLikeSequence does not support asynchronous iteration.');
};
/**
* Returns this same sequence. (Reversing an object-like sequence doesn't make
* any sense.)
*/
ObjectLikeSequence.prototype.reverse = function reverse() {
return this;
};
/**
* Returns an {@link 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.
*
* @public
* @aka extend
* @param {Object} other 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" }
*/
ObjectLikeSequence.prototype.assign = function assign(other) {
return new AssignSequence(this, other);
};
ObjectLikeSequence.prototype.extend = function extend(other) {
return this.assign(other);
};
/**
* @constructor
*/
function AssignSequence(parent, other) {
this.parent = parent;
this.other = other;
}
AssignSequence.prototype = new ObjectLikeSequence();
AssignSequence.prototype.get = function get(key) {
return this.other[key] || this.parent.get(key);
};
AssignSequence.prototype.each = function each(fn) {
var merged = new Set(),
done = false;
Lazy(this.other).each(function(value, key) {
if (fn(value, key) === false) {
done = true;
return false;
}
merged.add(key);
});
if (!done) {
return this.parent.each(function(value, key) {
if (!merged.contains(key) && fn(value, key) === false) {
return false;
}
});
}
};
/**
* Returns an {@link 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.
*
* @public
* @param {Object} defaults 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" }
*/
ObjectLikeSequence.prototype.defaults = function defaults(defaults) {
return new DefaultsSequence(this, defaults);
};
/**
* @constructor
*/
function DefaultsSequence(parent, defaults) {
this.parent = parent;
this.defaults = defaults;
}
DefaultsSequence.prototype = new ObjectLikeSequence();
DefaultsSequence.prototype.get = function get(key) {
return this.parent.get(key) || this.defaults[key];
};
DefaultsSequence.prototype.each = function each(fn) {
var merged = new Set(),
done = false;
this.parent.each(function(value, key) {
if (fn(value, key) === false) {
done = true;
return false;
}
if (typeof value !== "undefined") {
merged.add(key);
}
});
if (!done) {
Lazy(this.defaults).each(function(value, key) {
if (!merged.contains(key) && fn(value, key) === false) {
return false;
}
});
}
};
/**
* Returns an {@link ObjectLikeSequence} whose values are this sequence's keys,
* and whose keys are this sequence's values.
*
* @public
* @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" }
*/
ObjectLikeSequence.prototype.invert = function invert() {
return new InvertedSequence(this);
};
/**
* @constructor
*/
function InvertedSequence(parent) {
this.parent = parent;
}
InvertedSequence.prototype = new ObjectLikeSequence();
InvertedSequence.prototype.each = function each(fn) {
this.parent.each(function(value, key) {
return fn(key, value);
});
};
/**
* Produces an {@link ObjectLikeSequence} consisting of all the recursively
* merged values from this and the given object(s) or sequence(s).
*
* @public
* @param {...Object|ObjectLikeSequence} others The other object(s) or
* sequence(s) whose values will be merged into this one.
* @param {Function=} mergeFn An optional function used to customize merging
* behavior.
* @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}] }
*/
ObjectLikeSequence.prototype.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);
};
/**
* @constructor
*/
function MergedSequence(parent, others, mergeFn) {
this.parent = parent;
this.others = others;
this.mergeFn = mergeFn;
}
MergedSequence.prototype = new ObjectLikeSequence();
MergedSequence.prototype.each = function each(fn) {
var others = this.others,
mergeFn = this.mergeFn || mergeObjects,
keys = {};
var iteratedFullSource = this.parent.each(function(value, key) {
var merged = value;
forEach(others, function(other) {
if (key in other) {
merged = mergeFn(merged, other[key]);
}
});
keys[key] = true;
return fn(merged, key);
});
if (iteratedFullSource === false) {
return false;
}
var remaining = {};
forEach(others, function(other) {
for (var k in other) {
if (!keys[k]) {
remaining[k] = mergeFn(remaining[k], other[k]);
}
}
});
return Lazy(remaining).each(fn);
};
/**
* @private
* @examples
* mergeObjects({ foo: 1 }, { bar: 2 }); // => { foo: 1, bar: 2 }
* mergeObjects({ foo: { bar: 1 } }, { foo: { baz: 2 } }); // => { foo: { bar: 1, baz: 2 } }
* mergeObjects({ foo: { bar: 1 } }, { foo: undefined }); // => { foo: { bar: 1 } }
* mergeObjects({ foo: { bar: 1 } }, { foo: null }); // => { foo: null }
*/
function mergeObjects(a, b) {
if (typeof b === 'undefined') {
return a;
}
// Unless we're dealing with two objects, there's no merging to do --
// just replace a w/ b.
if (typeof a !== 'object' || a === null || typeof b !== 'object' || b === null) {
return b;
}
var merged = {}, prop;
for (prop in a) {
merged[prop] = mergeObjects(a[prop], b[prop]);
}
for (prop in b) {
if (!merged[prop]) {
merged[prop] = b[prop];
}
}
return merged;
}
/**
* Creates a {@link Sequence} consisting of the keys from this sequence whose
* values are functions.
*
* @public
* @aka methods
* @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"]
*/
ObjectLikeSequence.prototype.functions = function functions() {
return this
.filter(function(v, k) { return typeof(v) === "function"; })
.map(function(v, k) { return k; });
};
ObjectLikeSequence.prototype.methods = function methods() {
return this.functions();
};
/**
* Creates an {@link ObjectLikeSequence} consisting of the key/value pairs from
* this sequence whose keys are included in the given array of property names.
*
* @public
* @param {Array} properties 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" }
*/
ObjectLikeSequence.prototype.pick = function pick(properties) {
return new PickSequence(this, properties);
};
/**
* @constructor
*/
function PickSequence(parent, properties) {
this.parent = parent;
this.properties = properties;
}
PickSequence.prototype = new ObjectLikeSequence();
PickSequence.prototype.get = function get(key) {
return arrayContains(this.properties, key) ? this.parent.get(key) : undefined;
};
PickSequence.prototype.each = function each(fn) {
var inArray = arrayContains,
properties = this.properties;
return this.parent.each(function(value, key) {
if (inArray(properties, key)) {
return fn(value, key);
}
});
};
/**
* Creates an {@link ObjectLikeSequence} consisting of the key/value pairs from
* this sequence excluding those with the specified keys.
*
* @public
* @param {Array} properties 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" }
*/
ObjectLikeSequence.prototype.omit = function omit(properties) {
return new OmitSequence(this, properties);
};
/**
* @constructor
*/
function OmitSequence(parent, properties) {
this.parent = parent;
this.properties = properties;
}
OmitSequence.prototype = new ObjectLikeSequence();
OmitSequence.prototype.get = function get(key) {
return arrayContains(this.properties, key) ? undefined : this.parent.get(key);
};
OmitSequence.prototype.each = function each(fn) {
var inArray = arrayContains,
properties = this.properties;
return this.parent.each(function(value, key) {
if (!inArray(properties, key)) {
return fn(value, key);
}
});
};
/**
* Maps the key/value pairs in this sequence to arrays.
*
* @public
* @aka toArray
* @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"]]
*/
ObjectLikeSequence.prototype.pairs = function pairs() {
return this.map(function(v, k) { return [k, v]; });
};
/**
* Creates an array from the key/value pairs in this sequence.
*
* @public
* @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"]]
*/
ObjectLikeSequence.prototype.toArray = function toArray() {
return this.pairs().toArray();
};
/**
* Creates an object with the key/value pairs from this sequence.
*
* @public
* @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" }
*/
ObjectLikeSequence.prototype.toObject = function toObject() {
return this.reduce(function(object, value, key) {
object[key] = value;
return object;
}, {});
};
// Now that we've fully initialized the ObjectLikeSequence prototype, we can
// actually set the prototypes for GroupedSequence, IndexedSequence, and
// CountedSequence.
GroupedSequence.prototype = new ObjectLikeSequence();
GroupedSequence.prototype.each = function each(fn) {
var keyFn = createCallback(this.keyFn),
result;
result = this.parent.reduce(function(grouped,e) {
var key = keyFn(e);
if (!(grouped[key] instanceof Array)) {
grouped[key] = [e];
} else {
grouped[key].push(e);
}
return grouped;
},{});
return transform(function(grouped) {
for (var key in grouped) {
if (fn(grouped[key], key) === false) {
return false;
}
}
}, result);
};
IndexedSequence.prototype = new ObjectLikeSequence();
IndexedSequence.prototype.each = function each(fn) {
var keyFn = createCallback(this.keyFn),
indexed = {};
return this.parent.each(function(e) {
var key = keyFn(e);
if (!indexed[key]) {
indexed[key] = e;
return fn(e, key);
}
});
};
CountedSequence.prototype = new ObjectLikeSequence();
CountedSequence.prototype.each = function each(fn) {
var keyFn = createCallback(this.keyFn),
counted = {};
this.parent.each(function(e) {
var key = keyFn(e);
if (!counted[key]) {
counted[key] = 1;
} else {
counted[key] += 1;
}
});
for (var key in counted) {
if (fn(counted[key], key) === false) {
return false;
}
}
return true;
};
/**
* 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 {@link ObjectLikeSequence}.
*
* @public
* @param {(string|Array)=} propertyNames 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 }]
*/
ObjectLikeSequence.prototype.watch = function watch(propertyNames) {
throw new Error('You can only call #watch on a directly wrapped object.');
};
/**
* @constructor
*/
function ObjectWrapper(source) {
this.source = source;
}
ObjectWrapper.prototype = new ObjectLikeSequence();
ObjectWrapper.prototype.root = function root() {
return this;
};
ObjectWrapper.prototype.isAsync = function isAsync() {
return false;
};
ObjectWrapper.prototype.get = function get(key) {
return this.source[key];
};
ObjectWrapper.prototype.each = function each(fn) {
var source = this.source,
key;
for (key in source) {
if (fn(source[key], key) === false) {
return false;
}
}
return true;
};
/**
* 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`.
*
* @public
* @constructor
*
* @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
*/
function StringLikeSequence() {}
StringLikeSequence.prototype = new ArrayLikeSequence();
/**
* Create a new constructor function for a type inheriting from
* `StringLikeSequence`.
*
* @public
* @param {string|Array.<string>} methodName 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.
* @param {Object} overrides An object containing function overrides for this
* new sequence type. Has the same requirements as
* {@link 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"
*/
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);
};
StringLikeSequence.prototype.value = function value() {
return this.toString();
};
/**
* Returns an {@link IndexedIterator} that will step over each character in this
* sequence one by one.
*
* @returns {IndexedIterator} The iterator.
*/
StringLikeSequence.prototype.getIterator = function getIterator() {
return new CharIterator(this);
};
/**
* @constructor
*/
function CharIterator(source) {
this.source = Lazy(source);
this.index = -1;
}
CharIterator.prototype.current = function current() {
return this.source.charAt(this.index);
};
CharIterator.prototype.moveNext = function moveNext() {
return (++this.index < this.source.length());
};
/**
* 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.
*
* @public
* @param {number} i 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) // => ""
*/
StringLikeSequence.prototype.charAt = function charAt(i) {
return this.get(i);
};
/**
* Returns the character code at the given index of this sequence, or `NaN` if
* the index lies outside the bounds of the sequence.
*
* @public
* @param {number} i 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
*/
StringLikeSequence.prototype.charCodeAt = function charCodeAt(i) {
var char = this.charAt(i);
if (!char) { return NaN; }
return char.charCodeAt(0);
};
/**
* Returns a {@link 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.
*
* @public
* @param {number} start The index where this sequence should begin.
* @param {number=} stop 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"
*/
StringLikeSequence.prototype.substring = function substring(start, stop) {
return new StringSegment(this, start, stop);
};
/**
* @constructor
*/
function StringSegment(parent, start, stop) {
this.parent = parent;
this.start = Math.max(0, start);
this.stop = stop;
}
StringSegment.prototype = new StringLikeSequence();
StringSegment.prototype.get = function get(i) {
return this.parent.get(i + this.start);
};
StringSegment.prototype.length = function length() {
return (typeof this.stop === "number" ? this.stop : this.parent.length()) - this.start;
};
/**
* An optimized version of {@link Sequence#first} that returns another
* {@link StringLikeSequence} (or just the first character, if `count` is
* undefined).
*
* @public
* @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'
*/
StringLikeSequence.prototype.first = function first(count) {
if (typeof count === "undefined") {
return this.charAt(0);
}
return this.substring(0, count);
};
/**
* An optimized version of {@link Sequence#last} that returns another
* {@link StringLikeSequence} (or just the last character, if `count` is
* undefined).
*
* @public
* @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'
*/
StringLikeSequence.prototype.last = function last(count) {
if (typeof count === "undefined") {
return this.charAt(this.length() - 1);
}
return this.substring(this.length() - count);
};
StringLikeSequence.prototype.drop = function drop(count) {
return this.substring(count);
};
/**
* 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).
*
* @public
* @param {string} substring The substring to search for.
* @param {number=} startIndex 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
*/
StringLikeSequence.prototype.indexOf = function indexOf(substring, startIndex) {
return this.toString().indexOf(substring, startIndex);
};
/**
* 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.
*
* @public
* @param {string} substring The substring to search for.
* @param {number=} startIndex 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
*/
StringLikeSequence.prototype.lastIndexOf = function lastIndexOf(substring, startIndex) {
return this.toString().lastIndexOf(substring, startIndex);
};
/**
* Checks if this sequence contains a given substring.
*
* @public
* @param {string} substring 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
*/
StringLikeSequence.prototype.contains = function contains(substring) {
return this.indexOf(substring) !== -1;
};
/**
* Checks if this sequence ends with a given suffix.
*
* @public
* @param {string} suffix 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
*/
StringLikeSequence.prototype.endsWith = function endsWith(suffix) {
return this.substring(this.length() - suffix.length).toString() === suffix;
};
/**
* Checks if this sequence starts with a given prefix.
*
* @public
* @param {string} prefix 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
*/
StringLikeSequence.prototype.startsWith = function startsWith(prefix) {
return this.substring(0, prefix.length).toString() === prefix;
};
/**
* Converts all of the characters in this string to uppercase.
*
* @public
* @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'
*/
StringLikeSequence.prototype.toUpperCase = function toUpperCase() {
return this.mapString(function(char) { return char.toUpperCase(); });
};
/**
* Converts all of the characters in this string to lowercase.
*
* @public
* @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'
*/
StringLikeSequence.prototype.toLowerCase = function toLowerCase() {
return this.mapString(function(char) { return char.toLowerCase(); });
};
/**
* Maps the characters of this sequence onto a new {@link StringLikeSequence}.
*
* @public
* @param {Function} mapFn 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"
*/
StringLikeSequence.prototype.mapString = function mapString(mapFn) {
return new MappedStringLikeSequence(this, mapFn);
};
/**
* @constructor
*/
function MappedStringLikeSequence(parent, mapFn) {
this.parent = parent;
this.mapFn = mapFn;
}
MappedStringLikeSequence.prototype = new StringLikeSequence();
MappedStringLikeSequence.prototype.get = IndexedMappedSequence.prototype.get;
MappedStringLikeSequence.prototype.length = IndexedMappedSequence.prototype.length;
/**
* Returns a copy of this sequence that reads back to front.
*
* @public
*
* @examples
* Lazy("abcdefg").reverse() // sequence: "gfedcba"
*/
StringLikeSequence.prototype.reverse = function reverse() {
return new ReversedStringLikeSequence(this);
};
/**
* @constructor
*/
function ReversedStringLikeSequence(parent) {
this.parent = parent;
}
ReversedStringLikeSequence.prototype = new StringLikeSequence();
ReversedStringLikeSequence.prototype.get = IndexedReversedSequence.prototype.get;
ReversedStringLikeSequence.prototype.length = IndexedReversedSequence.prototype.length;
StringLikeSequence.prototype.toString = function toString() {
return this.join("");
};
/**
* Creates a {@link Sequence} comprising all of the matches for the specified
* pattern in the underlying string.
*
* @public
* @param {RegExp} pattern 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: []
*/
StringLikeSequence.prototype.match = function match(pattern) {
return new StringMatchSequence(this.source, pattern);
};
/**
* @constructor
*/
function StringMatchSequence(source, pattern) {
this.source = source;
this.pattern = pattern;
}
StringMatchSequence.prototype = new Sequence();
StringMatchSequence.prototype.getIterator = function getIterator() {
return new StringMatchIterator(this.source, this.pattern);
};
/**
* @constructor
*/
function StringMatchIterator(source, pattern) {
this.source = source;
this.pattern = cloneRegex(pattern);
}
StringMatchIterator.prototype.current = function current() {
return this.match[0];
};
StringMatchIterator.prototype.moveNext = function moveNext() {
return !!(this.match = this.pattern.exec(this.source));
};
/**
* Creates a {@link Sequence} comprising all of the substrings of this string
* separated by the given delimiter, which can be either a string or a regular
* expression.
*
* @public
* @param {string|RegExp} delimiter 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"]
*/
StringLikeSequence.prototype.split = function split(delimiter) {
return new SplitStringSequence(this.source, delimiter);
};
/**
* @constructor
*/
function SplitStringSequence(source, pattern) {
this.source = source;
this.pattern = pattern;
}
SplitStringSequence.prototype = new Sequence();
SplitStringSequence.prototype.getIterator = function getIterator() {
if (this.pattern instanceof RegExp) {
if (this.pattern.source === "" || this.pattern.source === "(?:)") {
return new CharIterator(this.source);
} else {
return new SplitWithRegExpIterator(this.source, this.pattern);
}
} else if (this.pattern === "") {
return new CharIterator(this.source);
} else {
return new SplitWithStringIterator(this.source, this.pattern);
}
};
/**
* @constructor
*/
function SplitWithRegExpIterator(source, pattern) {
this.source = source;
this.pattern = cloneRegex(pattern);
}
SplitWithRegExpIterator.prototype.current = function current() {
return this.source.substring(this.start, this.end);
};
SplitWithRegExpIterator.prototype.moveNext = function moveNext() {
if (!this.pattern) {
return false;
}
var match = this.pattern.exec(this.source);
if (match) {
this.start = this.nextStart ? this.nextStart : 0;
this.end = match.index;
this.nextStart = match.index + match[0].length;
return true;
} else if (this.pattern) {
this.start = this.nextStart;
this.end = undefined;
this.nextStart = undefined;
this.pattern = undefined;
return true;
}
return false;
};
/**
* @constructor
*/
function SplitWithStringIterator(source, delimiter) {
this.source = source;
this.delimiter = delimiter;
}
SplitWithStringIterator.prototype.current = function current() {
return this.source.substring(this.leftIndex, this.rightIndex);
};
SplitWithStringIterator.prototype.moveNext = function moveNext() {
if (!this.finished) {
this.leftIndex = typeof this.leftIndex !== "undefined" ?
this.rightIndex + this.delimiter.length :
0;
this.rightIndex = this.source.indexOf(this.delimiter, this.leftIndex);
}
if (this.rightIndex === -1) {
this.finished = true;
this.rightIndex = undefined;
return true;
}
return !this.finished;
};
/**
* Wraps a string exposing {@link #match} and {@link #split} methods that return
* {@link Sequence} objects instead of arrays, improving on the efficiency of
* JavaScript's built-in `String#split` and `String.match` methods and
* supporting asynchronous iteration.
*
* @param {string} source The string to wrap.
* @constructor
*/
function StringWrapper(source) {
this.source = source;
}
StringWrapper.prototype = new StringLikeSequence();
StringWrapper.prototype.root = function root() {
return this;
};
StringWrapper.prototype.isAsync = function isAsync() {
return false;
};
StringWrapper.prototype.get = function get(i) {
return this.source.charAt(i);
};
StringWrapper.prototype.length = function length() {
return this.source.length;
};
/**
* 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 {@link Lazy.generate}.
*
* @public
* @constructor
* @param {function(number):*} generatorFn A function which accepts an index
* and returns a value for the element at that position in the sequence.
* @param {number=} length The length of the sequence. If this argument is
* omitted, the sequence will go on forever.
*/
function GeneratedSequence(generatorFn, length) {
this.get = generatorFn;
this.fixedLength = length;
}
GeneratedSequence.prototype = new Sequence();
GeneratedSequence.prototype.isAsync = function isAsync() {
return false;
};
/**
* Returns the length of this sequence.
*
* @public
* @returns {number} The length, or `undefined` if this is an indefinite
* sequence.
*/
GeneratedSequence.prototype.length = function length() {
return this.fixedLength;
};
/**
* 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., {@link Sequence#take} to limit iteration).
*
* @public
* @param {Function} fn The function to call on each output from the generator
* function.
*/
GeneratedSequence.prototype.each = function each(fn) {
var generatorFn = this.get,
length = this.fixedLength,
i = 0;
while (typeof length === "undefined" || i < length) {
if (fn(generatorFn(i++)) === false) {
return false;
}
}
return true;
};
GeneratedSequence.prototype.getIterator = function getIterator() {
return new GeneratedIterator(this);
};
/**
* Iterates over a generated sequence. (This allows generated sequences to be
* iterated asynchronously.)
*
* @param {GeneratedSequence} sequence The generated sequence to iterate over.
* @constructor
*/
function GeneratedIterator(sequence) {
this.sequence = sequence;
this.index = 0;
this.currentValue = null;
}
GeneratedIterator.prototype.current = function current() {
return this.currentValue;
};
GeneratedIterator.prototype.moveNext = function moveNext() {
var sequence = this.sequence;
if (typeof sequence.fixedLength === "number" && this.index >= sequence.fixedLength) {
return false;
}
this.currentValue = sequence.get(this.index++);
return true;
};
/**
* An `AsyncSequence` iterates over its elements asynchronously when
* {@link #each} is called.
*
* You get an `AsyncSequence` by calling {@link 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 {@link Iterator}. This is an object whose prototype
* has the methods {@link Iterator#moveNext} (which returns a `boolean`) and
* {@link 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 {@link #each} on an `AsyncSequence` is to
* create an iterator and then asynchronously call {@link Iterator#moveNext}
* (using `setImmediate`, if available, otherwise `setTimeout`) until the iterator
* can't move ahead any more.
*
* @public
* @constructor
* @param {Sequence} parent A {@link Sequence} to wrap, to expose asynchronous
* iteration.
* @param {number=} interval 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.
*/
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);
}
AsyncSequence.prototype = new Sequence();
AsyncSequence.prototype.isAsync = function isAsync() {
return true;
};
/**
* Throws an exception. You cannot manually iterate over an asynchronous
* sequence.
*
* @public
* @example
* Lazy([1, 2, 3]).async().getIterator() // throws
*/
AsyncSequence.prototype.getIterator = function getIterator() {
throw new Error('An AsyncSequence does not support synchronous iteration.');
};
/**
* An asynchronous version of {@link Sequence#each}.
*
* @public
* @param {Function} fn The function to invoke asynchronously on each element in
* the sequence one by one.
* @returns {AsyncHandle} An {@link 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`).
*/
AsyncSequence.prototype.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;
};
/**
* An `AsyncHandle` provides a [Promises/A+](http://promises-aplus.github.io/promises-spec/)
* compliant interface for an {@link AsyncSequence} that is currently (or was)
* iterating over its elements.
*
* In addition to behaving as a promise, an `AsyncHandle` provides the ability
* to {@link AsyncHandle#cancel} iteration (if `cancelFn` is provided)
* and also offers convenient {@link AsyncHandle#onComplete} and
* {@link AsyncHandle#onError} methods to attach listeners for when iteration
* is complete or an error is thrown during iteration.
*
* @public
* @param {Function} cancelFn 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.).
* @constructor
*
* @example
* // 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) {
* console.log('Created array with ' + array.length + ' elements!');
* });
*
* // 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();
* });
*/
function AsyncHandle(cancelFn) {
this.resolveListeners = [];
this.rejectListeners = [];
this.state = PENDING;
this.cancelFn = cancelFn;
}
// Async handle states
var PENDING = 1,
RESOLVED = 2,
REJECTED = 3;
AsyncHandle.prototype.then = function then(onFulfilled, onRejected) {
var promise = new AsyncHandle(this.cancelFn);
this.resolveListeners.push(function(value) {
try {
if (typeof onFulfilled !== 'function') {
resolve(promise, value);
return;
}
resolve(promise, onFulfilled(value));
} catch (e) {
promise._reject(e);
}
});
this.rejectListeners.push(function(reason) {
try {
if (typeof onRejected !== 'function') {
promise._reject(reason);
return;
}
resolve(promise, onRejected(reason));
} catch (e) {
promise._reject(e);
}
});
if (this.state === RESOLVED) {
this._resolve(this.value);
}
if (this.state === REJECTED) {
this._reject(this.reason);
}
return promise;
};
AsyncHandle.prototype._resolve = function _resolve(value) {
if (this.state === REJECTED) {
return;
}
if (this.state === PENDING) {
this.state = RESOLVED;
this.value = value;
}
consumeListeners(this.resolveListeners, this.value);
};
AsyncHandle.prototype._reject = function _reject(reason) {
if (this.state === RESOLVED) {
return;
}
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
}
consumeListeners(this.rejectListeners, this.reason);
};
/**
* Cancels asynchronous iteration.
*
* @public
*/
AsyncHandle.prototype.cancel = function cancel() {
if (this.cancelFn) {
this.cancelFn();
this.cancelFn = null;
this._resolve(false);
}
};
/**
* Updates the handle with a callback to execute when iteration is completed.
*
* @public
* @param {Function} callback The function to call when the asynchronous
* iteration is completed.
* @return {AsyncHandle} A reference to the handle (for chaining).
*/
AsyncHandle.prototype.onComplete = function onComplete(callback) {
this.resolveListeners.push(callback);
return this;
};
/**
* Updates the handle with a callback to execute if/when any error is
* encountered during asynchronous iteration.
*
* @public
* @param {Function} callback The function to call, with any associated error
* object, when an error occurs.
* @return {AsyncHandle} A reference to the handle (for chaining).
*/
AsyncHandle.prototype.onError = function onError(callback) {
this.rejectListeners.push(callback);
return this;
};
/**
* Promise resolution procedure:
* http://promises-aplus.github.io/promises-spec/#the_promise_resolution_procedure
*/
function resolve(promise, x) {
if (promise === x) {
promise._reject(new TypeError('Cannot resolve a promise to itself'));
return;
}
if (x instanceof AsyncHandle) {
x.then(
function(value) { resolve(promise, value); },
function(reason) { promise._reject(reason); }
);
return;
}
var then;
try {
then = (/function|object/).test(typeof x) && x != null && x.then;
} catch (e) {
promise._reject(e);
return;
}
var thenableState = PENDING;
if (typeof then === 'function') {
try {
then.call(
x,
function resolvePromise(value) {
if (thenableState !== PENDING) {
return;
}
thenableState = RESOLVED;
resolve(promise, value);
},
function rejectPromise(reason) {
if (thenableState !== PENDING) {
return;
}
thenableState = REJECTED;
promise._reject(reason);
}
);
} catch (e) {
if (thenableState !== PENDING) {
return;
}
promise._reject(e);
}
return;
}
promise._resolve(x);
}
function consumeListeners(listeners, value, callback) {
callback || (callback = getOnNextCallback());
callback(function() {
if (listeners.length > 0) {
listeners.shift()(value);
consumeListeners(listeners, value, callback);
}
});
}
function getOnNextCallback(interval) {
if (typeof interval === "undefined") {
if (typeof setImmediate === "function") {
return setImmediate;
}
}
interval = interval || 0;
return function(fn) {
return setTimeout(fn, interval);
};
}
function getCancelCallback(interval) {
if (typeof interval === "undefined") {
if (typeof clearImmediate === "function") {
return clearImmediate;
}
}
return clearTimeout;
}
/**
* Transform a value, whether the value is retrieved asynchronously or directly.
*
* @private
* @param {Function} fn The function that transforms the value.
* @param {*} value The value to be transformed. This can be an {@link AsyncHandle} when the value
* is retrieved asynchronously, otherwise it can be anything.
* @returns {*} An {@link AsyncHandle} when `value` is also an {@link AsyncHandle}, otherwise
* whatever `fn` resulted in.
*/
function transform(fn, value) {
if (value instanceof AsyncHandle) {
return value.then(function() { fn(value); });
}
return fn(value);
}
/**
* An async version of {@link Sequence#reverse}.
*/
AsyncSequence.prototype.reverse = function reverse() {
return this.parent.reverse().async();
};
/**
* A version of {@link Sequence#find} which returns an {@link AsyncHandle}.
*
* @public
* @param {Function} predicate A function to call on (potentially) every element
* in the sequence.
* @returns {AsyncHandle} An {@link AsyncHandle} (promise) which resolves to
* the found element, once it is detected, or else `undefined`.
*/
AsyncSequence.prototype.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; });
};
/**
* A version of {@link Sequence#indexOf} which returns an {@link AsyncHandle}.
*
* @public
* @param {*} value The element to search for in the sequence.
* @returns {AsyncHandle} An {@link AsyncHandle} (promise) which resolves to
* the found index, once it is detected, or -1.
*/
AsyncSequence.prototype.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;
});
};
/**
* A version of {@link Sequence#contains} which returns an {@link AsyncHandle}.
*
* @public
* @param {*} value The element to search for in the sequence.
* @returns {AsyncHandle} An {@link AsyncHandle} (promise) which resolves to
* either `true` or `false` to indicate whether the element was found.
*/
AsyncSequence.prototype.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;
});
};
/**
* Just return the same sequence for `AsyncSequence#async` (I see no harm in this).
*/
AsyncSequence.prototype.async = function async() {
return this;
};
/**
* See {@link ObjectLikeSequence#watch} for docs.
*/
ObjectWrapper.prototype.watch = function watch(propertyNames) {
return new WatchedPropertySequence(this.source, propertyNames);
};
function WatchedPropertySequence(object, propertyNames) {
this.listeners = [];
if (!propertyNames) {
propertyNames = Lazy(object).keys().toArray();
} else if (!(propertyNames instanceof Array)) {
propertyNames = [propertyNames];
}
var listeners = this.listeners,
index = 0;
Lazy(propertyNames).each(function(propertyName) {
var propertyValue = object[propertyName];
Object.defineProperty(object, propertyName, {
get: function() {
return propertyValue;
},
set: function(value) {
for (var i = listeners.length - 1; i >= 0; --i) {
if (listeners[i]({ property: propertyName, value: value }, index) === false) {
listeners.splice(i, 1);
}
}
propertyValue = value;
++index;
}
});
});
}
WatchedPropertySequence.prototype = new AsyncSequence();
WatchedPropertySequence.prototype.each = function each(fn) {
this.listeners.push(fn);
};
/**
* A StreamLikeSequence comprises a sequence of 'chunks' of data, which are
* typically multiline strings.
*
* @constructor
*/
function StreamLikeSequence() {}
StreamLikeSequence.prototype = new AsyncSequence();
StreamLikeSequence.prototype.isAsync = function isAsync() {
return true;
};
StreamLikeSequence.prototype.split = function split(delimiter) {
return new SplitStreamSequence(this, delimiter);
};
/**
* @constructor
*/
function SplitStreamSequence(parent, delimiter) {
this.parent = parent;
this.delimiter = delimiter;
this.each = this.getEachForDelimiter(delimiter);
}
SplitStreamSequence.prototype = new Sequence();
SplitStreamSequence.prototype.getEachForDelimiter = function getEachForDelimiter(delimiter) {
if (delimiter instanceof RegExp) {
return this.regexEach;
}
return this.stringEach;
};
SplitStreamSequence.prototype.regexEach = function each(fn) {
var delimiter = cloneRegex(this.delimiter),
buffer = '',
start = 0, end,
index = 0;
var handle = this.parent.each(function(chunk) {
buffer += chunk;
var match;
while (match = delimiter.exec(buffer)) {
end = match.index;
if (fn(buffer.substring(start, end), index++) === false) {
return false;
}
start = end + match[0].length;
}
buffer = buffer.substring(start);
start = 0;
});
handle.onComplete(function() {
if (buffer.length > 0) {
fn(buffer);
}
});
return handle;
};
SplitStreamSequence.prototype.stringEach = function each(fn) {
var delimiter = this.delimiter,
pieceIndex = 0,
buffer = '',
bufferIndex = 0;
var handle = this.parent.each(function(chunk) {
buffer += chunk;
var delimiterIndex;
while ((delimiterIndex = buffer.indexOf(delimiter)) >= 0) {
var piece = buffer.substr(0,delimiterIndex);
buffer = buffer.substr(delimiterIndex+delimiter.length);
if (fn(piece,pieceIndex++) === false) {
return false;
}
}
return true;
});
handle.onComplete(function() {
fn(buffer, pieceIndex++);
});
return handle;
};
StreamLikeSequence.prototype.lines = function lines() {
return this.split("\n");
};
StreamLikeSequence.prototype.match = function match(pattern) {
return new MatchedStreamSequence(this, pattern);
};
/**
* @constructor
*/
function MatchedStreamSequence(parent, pattern) {
this.parent = parent;
this.pattern = cloneRegex(pattern);
}
MatchedStreamSequence.prototype = new AsyncSequence();
MatchedStreamSequence.prototype.each = function each(fn) {
var pattern = this.pattern,
done = false,
i = 0;
return this.parent.each(function(chunk) {
Lazy(chunk).match(pattern).each(function(match) {
if (fn(match, i++) === false) {
done = true;
return false;
}
});
return !done;
});
};
/**
* Defines a wrapper for custom {@link StreamLikeSequence}s. 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 {@link Sequence}. Hopefully the
* example will make this clear.
*
* @public
* @param {Function} initializer An initialization function called on objects
* created by this factory. `this` will be bound to the created object,
* which is an instance of {@link StreamLikeSequence}. Use `emit` to
* generate data for the sequence.
* @returns {Function} A function that creates a new {@link StreamLikeSequence},
* initializes it using the specified function, and returns it.
*
* @example
* 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']
*/
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;
};
};
/**
* Creates a {@link GeneratedSequence} using the specified generator function
* and (optionally) length.
*
* @public
* @param {function(number):*} generatorFn 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.
* @param {number=} length 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
*/
Lazy.generate = function generate(generatorFn, length) {
return new GeneratedSequence(generatorFn, length);
};
/**
* Creates a sequence from a given starting value, up to a specified stopping
* value, incrementing by a given step.
*
* @public
* @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 = function range() {
var start = arguments.length > 1 ? arguments[0] : 0,
stop = arguments.length > 1 ? arguments[1] : arguments[0],
step = arguments.length > 2 ? arguments[2] : 1;
return this.generate(function(i) { return start + (step * i); })
.take(Math.floor((stop - start) / step));
};
/**
* Creates a sequence consisting of the given value repeated a specified number
* of times.
*
* @public
* @param {*} value The value to repeat.
* @param {number=} count 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"]
*/
Lazy.repeat = function repeat(value, count) {
return Lazy.generate(function() { return value; }, count);
};
Lazy.Sequence = Sequence;
Lazy.ArrayLikeSequence = ArrayLikeSequence;
Lazy.ObjectLikeSequence = ObjectLikeSequence;
Lazy.StringLikeSequence = StringLikeSequence;
Lazy.StreamLikeSequence = StreamLikeSequence;
Lazy.GeneratedSequence = GeneratedSequence;
Lazy.AsyncSequence = AsyncSequence;
Lazy.AsyncHandle = AsyncHandle;
/*** Useful utility methods ***/
/**
* Creates a shallow copy of an array or object.
*
* @examples
* var array = [1, 2, 3], clonedArray,
* object = { foo: 1, bar: 2 }, clonedObject;
*
* clonedArray = Lazy.clone(array); // => [1, 2, 3]
* clonedArray.push(4); // clonedArray == [1, 2, 3, 4]
* array; // => [1, 2, 3]
*
* clonedObject = Lazy.clone(object); // => { foo: 1, bar: 2 }
* clonedObject.baz = 3; // clonedObject == { foo: 1, bar: 2, baz: 3 }
* object; // => { foo: 1, bar: 2 }
*/
Lazy.clone = function clone(target) {
return Lazy(target).value();
};
/**
* Marks a method as deprecated, so calling it will issue a console warning.
*/
Lazy.deprecate = function deprecate(message, fn) {
return function() {
console.warn(message);
return fn.apply(this, arguments);
};
};
var arrayPop = Array.prototype.pop,
arraySlice = Array.prototype.slice;
/**
* Creates a callback... you know, Lo-Dash style.
*
* - for functions, just returns the function
* - for strings, returns a pluck-style callback
* - for objects, returns a where-style callback
*
* @private
* @param {Function|string|Object} callback A function, string, or object to
* convert to a callback.
* @param {*} defaultReturn If the callback is undefined, a default return
* value to use for the function.
* @returns {Function} The callback function.
*
* @examples
* createCallback(function() {}) // instanceof Function
* createCallback('foo') // instanceof Function
* createCallback('foo')({ foo: 'bar'}) // => 'bar'
* createCallback({ foo: 'bar' })({ foo: 'bar' }) // => true
* createCallback({ foo: 'bar' })({ foo: 'baz' }) // => false
*/
function createCallback(callback, defaultValue) {
switch (typeof callback) {
case "function":
return callback;
case "string":
return function(e) {
return e[callback];
};
case "object":
return function(e) {
return Lazy(callback).all(function(value, key) {
return e[key] === value;
});
};
case "undefined":
return defaultValue ?
function() { return defaultValue; } :
Lazy.identity;
default:
throw new Error("Don't know how to make a callback from a " + typeof callback + "!");
}
}
/**
* Takes a function that returns a value for one argument and produces a
* function that compares two arguments.
*
* @private
* @param {Function|string|Object} callback A function, string, or object to
* convert to a callback using `createCallback`.
* @returns {Function} A function that accepts two values and returns 1 if
* the first is greater, -1 if the second is greater, or 0 if they are
* equivalent.
*
* @examples
* createComparator('a')({ a: 1 }, { a: 2 }); // => -1
* createComparator('a')({ a: 6 }, { a: 2 }); // => 1
* createComparator('a')({ a: 1 }, { a: 1 }); // => 0
* createComparator()(3, 5); // => -1
* createComparator()(7, 5); // => 1
* createComparator()(3, 3); // => 0
*/
function createComparator(callback, descending) {
if (!callback) { return compare; }
callback = createCallback(callback);
return function(x, y) {
return compare(callback(x), callback(y));
};
}
/**
* Takes a function and returns a function with the same logic but the
* arguments reversed. Only applies to functions w/ arity=2 as this is private
* and I can do what I want.
*
* @private
* @param {Function} fn The function to "reverse"
* @returns {Function} The "reversed" function
*
* @examples
* reverseArguments(function(x, y) { return x + y; })('a', 'b'); // => 'ba'
*/
function reverseArguments(fn) {
return function(x, y) { return fn(y, x); };
}
/**
* Creates a Set containing the specified values.
*
* @param {...Array} values One or more array(s) of values used to populate the
* set.
* @returns {Set} A new set containing the values passed in.
*/
function createSet(values) {
var set = new Set();
Lazy(values || []).flatten().each(function(e) {
set.add(e);
});
return set;
}
/**
* Compares two elements for sorting purposes.
*
* @private
* @param {*} x The left element to compare.
* @param {*} y The right element to compare.
* @returns {number} 1 if x > y, -1 if x < y, or 0 if x and y are equal.
*
* @examples
* compare(1, 2) // => -1
* compare(1, 1) // => 0
* compare(2, 1) // => 1
* compare('a', 'b') // => -1
*/
function compare(x, y) {
if (x === y) {
return 0;
}
return x > y ? 1 : -1;
}
/**
* Iterates over every element in an array.
*
* @param {Array} array The array.
* @param {Function} fn The function to call on every element, which can return
* false to stop the iteration early.
* @returns {boolean} True if every element in the entire sequence was iterated,
* otherwise false.
*/
function forEach(array, fn) {
var i = -1,
len = array.length;
while (++i < len) {
if (fn(array[i], i) === false) {
return false;
}
}
return true;
}
function getFirst(sequence) {
var result;
sequence.each(function(e) {
result = e;
return false;
});
return result;
}
/**
* Checks if an element exists in an array.
*
* @private
* @param {Array} array
* @param {*} element
* @returns {boolean} Whether or not the element exists in the array.
*
* @examples
* arrayContains([1, 2], 2) // => true
* arrayContains([1, 2], 3) // => false
* arrayContains([undefined], undefined) // => true
* arrayContains([NaN], NaN) // => true
*/
function arrayContains(array, element) {
var i = -1,
length = array.length;
// Special handling for NaN
if (element !== element) {
while (++i < length) {
if (array[i] !== array[i]) {
return true;
}
}
return false;
}
while (++i < length) {
if (array[i] === element) {
return true;
}
}
return false;
}
/**
* Checks if an element exists in an array before a given index.
*
* @private
* @param {Array} array
* @param {*} element
* @param {number} index
* @param {Function} keyFn
* @returns {boolean}
*
* @examples
* arrayContainsBefore([1, 2, 3], 3, 2) // => false
* arrayContainsBefore([1, 2, 3], 3, 3) // => true
*/
function arrayContainsBefore(array, element, index, keyFn) {
var i = -1;
if (keyFn) {
keyFn = createCallback(keyFn);
while (++i < index) {
if (keyFn(array[i]) === keyFn(element)) {
return true;
}
}
} else {
while (++i < index) {
if (array[i] === element) {
return true;
}
}
}
return false;
}
/**
* Swaps the elements at two specified positions of an array.
*
* @private
* @param {Array} array
* @param {number} i
* @param {number} j
*
* @examples
* var array = [1, 2, 3, 4, 5];
*
* swap(array, 2, 3) // array == [1, 2, 4, 3, 5]
*/
function swap(array, i, j) {
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
/**
* "Clones" a regular expression (but makes it always global).
*
* @private
* @param {RegExp|string} pattern
* @returns {RegExp}
*/
function cloneRegex(pattern) {
return eval("" + pattern + (!pattern.global ? "g" : ""));
};
/**
* A collection of unique elements.
*
* @private
* @constructor
*
* @examples
* var set = new Set(),
* obj1 = {},
* obj2 = {},
* fn1 = function fn1() {},
* fn2 = function fn2() {};
*
* set.add('foo') // => true
* set.add('foo') // => false
* set.add(1) // => true
* set.add(1) // => false
* set.add('1') // => true
* set.add('1') // => false
* set.add(obj1) // => true
* set.add(obj1) // => false
* set.add(obj2) // => true
* set.add(fn1) // => true
* set.add(fn2) // => true
* set.add(fn2) // => false
* set.contains('__proto__') // => false
* set.add('__proto__') // => true
* set.add('__proto__') // => false
* set.contains('add') // => false
* set.add('add') // => true
* set.add('add') // => false
* set.contains(undefined) // => false
* set.add(undefined) // => true
* set.contains(undefined) // => true
* set.contains('undefined') // => false
* set.add('undefined') // => true
* set.contains('undefined') // => true
* set.contains(NaN) // => false
* set.add(NaN) // => true
* set.contains(NaN) // => true
* set.contains('NaN') // => false
* set.add('NaN') // => true
* set.contains('NaN') // => true
* set.contains('@foo') // => false
* set.add('@foo') // => true
* set.contains('@foo') // => true
*/
function Set() {
this.table = {};
this.objects = [];
}
/**
* Attempts to add a unique value to the set.
*
* @param {*} value The value to add.
* @returns {boolean} True if the value was added to the set (meaning an equal
* value was not already present), or else false.
*/
Set.prototype.add = function add(value) {
var table = this.table,
type = typeof value,
// only applies for strings
firstChar,
// only applies for objects
objects;
switch (type) {
case "number":
case "boolean":
case "undefined":
if (!table[value]) {
table[value] = true;
return true;
}
return false;
case "string":
// Essentially, escape the first character if it could possibly collide
// with a number, boolean, or undefined (or a string that happens to start
// with the escape character!), OR if it could override a special property
// such as '__proto__' or 'constructor'.
switch (value.charAt(0)) {
case "_": // e.g., __proto__
case "f": // for 'false'
case "t": // for 'true'
case "c": // for 'constructor'
case "u": // for 'undefined'
case "@": // escaped
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case "N": // for NaN
value = "@" + value;
}
if (!table[value]) {
table[value] = true;
return true;
}
return false;
default:
// For objects and functions, we can't really do anything other than store
// them in an array and do a linear search for reference equality.
objects = this.objects;
if (!arrayContains(objects, value)) {
objects.push(value);
return true;
}
return false;
}
};
/**
* Checks whether the set contains a value.
*
* @param {*} value The value to check for.
* @returns {boolean} True if the set contains the value, or else false.
*/
Set.prototype.contains = function contains(value) {
var type = typeof value,
// only applies for strings
firstChar;
switch (type) {
case "number":
case "boolean":
case "undefined":
return !!this.table[value];
case "string":
// Essentially, escape the first character if it could possibly collide
// with a number, boolean, or undefined (or a string that happens to start
// with the escape character!), OR if it could override a special property
// such as '__proto__' or 'constructor'.
switch (value.charAt(0)) {
case "_": // e.g., __proto__
case "f": // for 'false'
case "t": // for 'true'
case "c": // for 'constructor'
case "u": // for 'undefined'
case "@": // escaped
case "0":
case "1":
case "2":
case "3":
case "4":
case "5":
case "6":
case "7":
case "8":
case "9":
case "N": // for NaN
value = "@" + value;
}
return !!this.table[value];
default:
// For objects and functions, we can't really do anything other than store
// them in an array and do a linear search for reference equality.
return arrayContains(this.objects, value);
}
};
/**
* A "rolling" queue, with a fixed capacity. As items are added to the head,
* excess items are dropped from the tail.
*
* @private
* @constructor
*
* @examples
* var queue = new Queue(3);
*
* queue.add(1).toArray() // => [1]
* queue.add(2).toArray() // => [1, 2]
* queue.add(3).toArray() // => [1, 2, 3]
* queue.add(4).toArray() // => [2, 3, 4]
* queue.add(5).add(6).toArray() // => [4, 5, 6]
* queue.add(7).add(8).toArray() // => [6, 7, 8]
*
* // also want to check corner cases
* new Queue(1).add('foo').add('bar').toArray() // => ['bar']
* new Queue(0).add('foo').toArray() // => []
* new Queue(-1) // throws
*
* @benchmarks
* function populateQueue(count, capacity) {
* var q = new Queue(capacity);
* for (var i = 0; i < count; ++i) {
* q.add(i);
* }
* }
*
* function populateArray(count, capacity) {
* var arr = [];
* for (var i = 0; i < count; ++i) {
* if (arr.length === capacity) { arr.shift(); }
* arr.push(i);
* }
* }
*
* populateQueue(100, 10); // populating a Queue
* populateArray(100, 10); // populating an Array
*/
function Queue(capacity) {
this.contents = new Array(capacity);
this.start = 0;
this.count = 0;
}
/**
* Adds an item to the queue, and returns the queue.
*/
Queue.prototype.add = function add(element) {
var contents = this.contents,
capacity = contents.length,
start = this.start;
if (this.count === capacity) {
contents[start] = element;
this.start = (start + 1) % capacity;
} else {
contents[this.count++] = element;
}
return this;
};
/**
* Returns an array containing snapshot of the queue's contents.
*/
Queue.prototype.toArray = function toArray() {
var contents = this.contents,
start = this.start,
count = this.count;
var snapshot = contents.slice(start, start + count);
if (snapshot.length < count) {
snapshot = snapshot.concat(contents.slice(0, count - snapshot.length));
}
return snapshot;
};
/**
* Shared base method for defining new sequence types.
*/
function defineSequenceType(base, name, overrides) {
/** @constructor */
var ctor = function ctor() {};
// Make this type inherit from the specified base.
ctor.prototype = new base();
// Attach overrides to the new sequence type's prototype.
for (var override in overrides) {
ctor.prototype[override] = overrides[override];
}
// Define a factory method that sets the new sequence's parent to the caller
// and (optionally) applies any additional initialization logic.
// Expose this as a chainable method so that we can do:
// Lazy(...).map(...).filter(...).blah(...);
var factory = function factory() {
var sequence = new ctor();
// Every sequence needs a reference to its parent in order to work.
sequence.parent = this;
// If a custom init function was supplied, call it now.
if (sequence.init) {
sequence.init.apply(sequence, arguments);
}
return sequence;
};
var methodNames = typeof name === 'string' ? [name] : name;
for (var i = 0; i < methodNames.length; ++i) {
base.prototype[methodNames[i]] = factory;
}
return ctor;
}
/*** Exposing Lazy to the world ***/
// For Node.js
if (typeof module === "object" && module && module.exports === context) {
module.exports = Lazy;
// For browsers
} else {
context.Lazy = Lazy;
}
}(this));
</script>
var data = [
["Mayme White", 1610, 127, 89, "Western Gas & Electric PC", "a"],
["Lenora Soto", 6282, 531, 18, "Initrode Consulting", "a"],
["Mathilda Briggs", 102, 648, 46, "Chez Quis Limited", "b"],
["Martha Henry", 6924, 795, 9, "Primatech LLP", "b"],
["Ola Cruz", 9678, 679, 99, "Globex Corporation Foundation", "e"],
["Luis Tran", 2521, 931, 26, "Chasers LLC", "a"],
["Brian McGee", 3776, 91, 37, "Dunder Mifflin Company", "d"],
["Howard Harrington", 8236, 973, 26, "Central Perk Corp.", "e"],
["Curtis Lee", 2483, 151, 77, "Burleigh and Stronginthearm Corp.", "e"],
["Maurice Park", 885, 957, 82, "General Forge and Foundry Corp.", "c"],
["Blake Fisher", 1393, 544, 72, "Leeding Engines Ltd. LLP", "c"],
["Jeffery Blair", 7686, 508, 45, "Big T Burgers and Fries Associates", "a"],
["Luis Rowe", 8829, 733, 29, "Bluth Company Corp.", "b"],
["Olive Cobb", 9523, 749, 69, "Leeding Engines Ltd. LLC", "d"],
["Justin Neal", 9129, 882, 79, "Globo-Chem LLC", "e"],
["Jon King", 5275, 246, 36, "Initrode Inc.", "b"],
["Isabel Luna", 382, 772, 44, "Powell Motors Corporation", "c"],
["Jerry Bowman", 8772, 613, 17, "Osato Chemicals Corporation", "c"],
["Carlos Walsh", 6210, 709, 32, "Allied Biscuit Company", "a"],
["Ruth Larson", 4352, 529, 9, "Western Gas & Electric Limited", "e"],
["Stanley Norris", 2372, 620, 55, "Initech Corp.", "d"],
["Craig Franklin", 5012, 481, 30, "Corellian Engineering Corporation Corp.", "b"],
["Hunter Burke", 8160, 545, 45, "Mainway Toys Corporation", "d"],
["Devin Parsons", 5492, 709, 16, "General Products Company", "d"],
["Ina Neal", 4611, 534, 66, "Blammo Consulting", "a"],
["Leon Schneider", 1462, 328, 85, "Moes Tavern PC", "b"],
["Hilda Santos", 729, 531, 19, "Mammoth Pictures LLP", "d"],
["Calvin Herrera", 2248, 702, 77, "The Krusty Krab Industries", "d"],
["Lucas Malone", 942, 186, 9, "Chez Quis LLP", "a"],
["Viola Gutierrez", 8956, 60, 91, "Corellian Engineering Corporation LLP", "d"],
["Lelia Blake", 5696, 772, 59, "Sirius Cybernetics Corporation Inc.", "c"],
["Nell French", 7840, 650, 10, "Compuglobalhypermeganet Inc.", "d"],
["Mabel Sims", 4082, 381, 9, "Dunder Mifflin Inc.", "b"],
["Susan Lopez", 5651, 559, 41, "Spade and Archer Industries", "b"],
["Nelle Foster", 5535, 477, 81, "Megadodo Publications Corporation", "d"],
["Shane Griffin", 3377, 841, 66, "Burleigh and Stronginthearm LLP", "e"],
["Laura Hayes", 852, 874, 84, "Colonial Movers Consulting", "c"],
["Bill Alvarado", 2509, 789, 37, "Compuglobalhypermeganet Associates", "c"],
["Gilbert Wise", 6596, 759, 13, "United Fried Chicken Company", "e"],
["Oscar Patrick", 9644, 162, 20, "Water and Power Associates", "d"],
["Isabella Jefferson", 100, 154, 48, "Burleigh and Stronginthearm Limited", "b"],
["Lester Goodwin", 4938, 101, 27, "Vandelay Industries LLC", "b"],
["Hester Fox", 360, 59, 47, "Gadgetron Industries", "c"],
["Mike Higgins", 5317, 979, 42, "McMahon and Tate Ltd", "d"],
["Augusta Carson", 1736, 951, 72, "Central Perk LLC", "d"],
["Maggie Norton", 252, 259, 98, "Cogswell Cogs Consulting", "c"],
["Marc Olson", 746, 102, 57, "Extensive Enterprise PC", "c"],
["Effie Pope", 896, 216, 5, "Leeding Engines Ltd. Consulting", "b"],
["Lelia Brooks", 6111, 256, 27, "Smith and Co. Consulting", "c"],
["Brian Lucas", 1125, 195, 40, "ZiffCorp Industries", "b"],
["Lee Hale", 7980, 260, 94, "LuthorCorp Corp.", "a"],
["Roy Gordon", 3398, 183, 45, "SpringShield LLC", "c"],
["Alberta Gregory", 5183, 820, 32, "Wernham Hogg LLC", "c"],
["Janie Jackson", 2618, 110, 60, "Initrode Incorporated", "e"],
["George Schwartz", 558, 988, 60, "Sample Company Institute", "c"],
["Dylan Mann", 1943, 828, 38, "Powell Motors Institute", "c"],
["Billy Todd", 4889, 309, 88, "The Frying Dutchman Institute", "c"],
["Ollie Adams", 5600, 220, 4, "Fake Brothers LLC", "a"],
["Clayton Gregory", 1110, 232, 71, "Wernham Hogg Ltd", "a"],
["Theodore Baker", 9470, 215, 69, "Omni Consimer Products LLC", "c"],
["Josephine Hoffman", 2212, 602, 9, "North Central Positronics Industries", "d"],
["Jacob Freeman", 4208, 327, 95, "Krustyco Company", "e"],
["Edgar Mack", 9721, 27, 96, "QWERTY Logistics Institute", "b"],
["Nell Ramos", 734, 1000, 33, "Sample Company Corp.", "a"],
["Leonard Williams", 806, 42, 93, "Spade and Archer LLP", "b"],
["Carlos Jacobs", 1372, 163, 12, "Rouster and Sidewazys Ltd", "e"],
["Trevor Cruz", 826, 586, 27, "Virtucon Corporation", "c"],
["Edna Mason", 650, 437, 20, "Smith and Co. Inc.", "d"],
["Eric Carr", 8019, 36, 70, "Big T Burgers and Fries Consulting", "d"],
["Lois Nash", 8245, 125, 16, "Central Perk Limited", "d"],
["Gussie Schmidt", 3692, 368, 83, "Gadgetron Company", "c"],
["Darrell Bennett", 5109, 985, 12, "Mammoth Pictures LLP", "e"],
["Olivia Cain", 8585, 607, 55, "U.S. Robotics and Mechanical Men Foundation", "d"],
["Shane Hubbard", 646, 32, 28, "Initrode Consulting", "b"],
["Elva Soto", 8540, 187, 83, "Data Systems Corp.", "e"],
["Mike Goodman", 9050, 606, 1, "Dunder Mifflin Institute", "a"],
["Henrietta Green", 9325, 313, 24, "Wernham Hogg Incorporated", "a"],
["Brent Carroll", 4186, 88, 81, "Praxis Corporation Institute", "e"],
["Eva Horton", 8386, 138, 64, "Taggart Transcontinental Incorporated", "b"],
["Peter Bryan", 7462, 745, 18, "Chez Quis Consulting", "b"],
["Roy Coleman", 1555, 151, 94, "Leeding Engines Ltd. Industries", "b"],
["Elsie Steele", 2580, 506, 59, "Charles Townsend Agency Consulting", "e"],
["Evan Park", 2018, 448, 22, "Universal Export Corp.", "e"],
["Paul Bryant", 9511, 844, 26, "Videlectrix LLC", "d"],
["Hester Curry", 3057, 516, 86, "Blammo LLP", "d"],
["Carrie Richardson", 4869, 160, 86, "Stay Puft Corporation LLC", "c"],
["Elva Harris", 3504, 885, 84, "BLAND Corporation LLP", "c"],
["Brett Rose", 6344, 322, 92, "C.H. Lavatory and Sons Incorporated", "c"],
["Don Abbott", 8343, 587, 48, "Sombra Corporation Institute", "e"],
["Elmer Quinn", 9320, 329, 39, "Spade and Archer PC", "a"],
["Hannah Kelly", 8752, 231, 21, "Allied Biscuit Company", "d"],
["Travis Cortez", 2200, 419, 64, "Demo Company Foundation", "c"],
["Owen Porter", 8705, 877, 60, "Sample Company Associates", "d"],
["Virgie Douglas", 5761, 677, 34, "Burleigh and Stronginthearm Company", "d"],
["Bertie Waters", 4966, 451, 91, "Charles Townsend Agency Corporation", "e"],
["Isaac Zimmerman", 9830, 187, 6, "Chotchkies Incorporated", "b"],
["Olga Porter", 9360, 418, 48, "Niagular Company", "d"],
["Theresa Erickson", 7041, 949, 23, "U.S. Robotics and Mechanical Men Limited", "d"],
["Terry Riley", 2010, 108, 39, "Thrift Bank LLP", "d"],
["Eliza McGee", 2023, 9, 62, "Stay Puft Corporation Corp.", "c"],
["Estelle Brewer", 5910, 569, 56, "Data Systems Incorporated", "a"],
["Gilbert Holloway", 9091, 815, 65, "Colonial Movers LLP", "c"],
["Isabella Garner", 2630, 955, 44, "Milliways Institute", "c"],
["Caleb Rhodes", 1030, 506, 67, "Galaxy Corp Consulting", "c"],
["Brent Delgado", 5975, 579, 10, "Colonial Movers Inc.", "b"],
["Dollie Quinn", 3730, 275, 60, "123 Warehousing Ltd", "b"],
["Johanna Woods", 3666, 0, 18, "Nordyne Defense Dynamics Corporation", "a"],
["Nathaniel Grant", 3330, 42, 73, "Osato Chemicals Industries", "c"],
["Loretta Peters", 598, 821, 77, "Praxis Corporation LLP", "a"],
["Robert Jones", 2873, 60, 92, "Charles Townsend Agency Limited", "c"],
["Hester McCoy", 8626, 822, 42, "Monks Diner Ltd", "e"],
["Maggie Rios", 8271, 482, 22, "Globo-Chem Corp.", "a"],
["Anthony McGee", 7691, 599, 100, "Galaxy Corp Corporation", "c"],
["Rena Wise", 632, 530, 67, "Petrox Oil Company Institute", "e"],
["Cornelia Daniel", 9238, 141, 3, "BLAND Corporation Consulting", "e"],
["Darrell Bryant", 1647, 407, 73, "General Forge and Foundry Limited", "c"],
["Jesus Wade", 5924, 951, 26, "SpringShield Company", "c"],
["Hulda McDaniel", 1197, 1000, 89, "United Fried Chicken Ltd", "b"],
["Daniel Delgado", 9848, 533, 53, "Omni Consimer Products LLP", "d"],
["Leila Smith", 2288, 827, 74, "Mammoth Pictures Institute", "a"],
["Lucinda Fitzgerald", 1578, 496, 42, "Initrode Incorporated", "d"],
["Dylan Bowen", 9553, 897, 56, "Sirius Cybernetics Corporation Consulting", "b"],
["Joe Bowman", 3119, 298, 11, "Tip Top Cafe Corp.", "a"],
["Beulah Owen", 4743, 353, 90, "Spacely Sprockets Consulting", "a"],
["Wesley Simpson", 6890, 1, 27, "Gadgetron Inc.", "d"],
["Mildred Washington", 7628, 686, 28, "North Central Positronics LLC", "d"],
["Harry Schmidt", 8092, 855, 51, "Petrox Oil Company LLP", "e"],
["Minerva Simon", 4318, 47, 68, "Petrox Oil Company LLC", "c"],
["Lucas McCoy", 9739, 356, 19, "Spade and Archer Institute", "c"],
["Louis Farmer", 1808, 646, 2, "Three Waters Industries", "b"],
["Jose Lawson", 3114, 548, 60, "Galaxy Corp LLP", "e"],
["Loretta Austin", 9336, 839, 4, "123 Warehousing LLP", "c"],
["Marie Brewer", 5721, 743, 79, "Duff Brewing Company LLP", "e"],
["Lucinda Ramsey", 1842, 263, 26, "Gizmonic Institute Industries", "e"],
["Lizzie Rodgers", 2517, 208, 21, "The Legitimate Businessmens Club LLP", "d"],
["Theodore Erickson", 9010, 77, 1, "Industrial Automation Associates", "b"],
["Jorge Abbott", 9213, 247, 65, "Globo Gym American Corp Corporation", "c"],
["Brian Strickland", 2198, 26, 78, "Praxis Corporation LLC", "b"],
["Arthur Pratt", 7151, 837, 43, "Duff Brewing Company LLP", "c"],
["Leo Hubbard", 2862, 261, 60, "Wernham Hogg Corporation", "e"],
["Jay Collier", 7531, 780, 53, "Praxis Corporation Ltd", "b"],
["Essie Patterson", 9628, 141, 54, "Monarch Playing Card Co. Company", "c"],
["Loretta Mason", 3232, 443, 77, "Chasers Ltd", "c"],
["Eleanor Diaz", 8505, 863, 61, "Charles Townsend Agency LLP", "e"],
["Bryan Edwards", 3790, 710, 42, "Moes Tavern PC", "b"],
["Nell Jacobs", 4989, 207, 0, "Input Inc. Company", "c"],
["Flora McCoy", 7601, 142, 95, "Transworld Consortium Foundation", "c"],
["Edgar Sparks", 4812, 360, 80, "Sto Plains Holdings PC", "b"],
["Evelyn Carson", 2475, 909, 40, "The Legitimate Businessmens Club PC", "d"],
["Mattie Moss", 9637, 715, 8, "Cogswell Cogs LLP", "a"],
["Timothy Bell", 2853, 191, 59, "ABC Telecom PC", "a"],
["Sarah Mitchell", 4582, 455, 56, "Spade and Archer Foundation", "d"],
["Jeremy Harrington", 6129, 190, 75, "Ankh-Sto Associates LLC", "b"],
["Mabelle Hayes", 4638, 172, 42, "Cogswell Cogs LLC", "d"],
["Susie Bryan", 6497, 786, 0, "Big Kahuna Burger Foundation", "b"],
["Teresa Stanley", 8702, 823, 85, "Sonky Rubber Goods Corp.", "d"],
["Dustin Ramsey", 6990, 634, 97, "The Legitimate Businessmens Club LLP", "a"],
["Joel Chapman", 3727, 971, 6, "Atlantic Northern PC", "b"],
["Anthony May", 1609, 271, 55, "Transworld Consortium LLP", "e"],
["Julian Turner", 9458, 833, 63, "Kumatsu Motors Company", "b"],
["Fanny Johnston", 9169, 723, 40, "SpringShield Incorporated", "e"],
["Claudia Larson", 4343, 145, 60, "Keedsler Motors Limited", "d"],
["Sallie McBride", 8859, 892, 95, "Fake Brothers Foundation", "e"],
["Mitchell Larson", 5840, 192, 87, "The Frying Dutchman Industries", "c"],
["Jeffery Boyd", 3001, 364, 40, "Big Kahuna Burger PC", "d"],
["Danny Santos", 9592, 258, 41, "Kumatsu Motors Limited", "c"],
["Christopher Rice", 561, 886, 30, "Globo-Chem LLP", "a"],
["Franklin Ramirez", 8277, 686, 12, "Chasers Limited", "d"],
["Ruth Soto", 1940, 513, 24, "Kumatsu Motors Industries", "c"],
["Norman Reyes", 4696, 935, 2, "Nordyne Defense Dynamics LLP", "c"],
["Evan Reeves", 8255, 602, 19, "Petrox Oil Company Incorporated", "d"],
["Justin Wright", 4457, 19, 3, "Spade and Archer Limited", "b"],
["Polly Wright", 4026, 867, 56, "Mr. Sparkle PC", "a"],
["Vincent Hoffman", 6589, 326, 12, "Allied Biscuit Ltd", "d"],
["Max Weber", 3206, 808, 12, "Sixty Second Avenue Industries", "b"],
["Mae Fernandez", 8770, 508, 20, "The Krusty Krab Consulting", "a"],
["Brent Chambers", 6446, 27, 64, "Praxis Corporation Ltd", "d"],
["Kevin Meyer", 4044, 674, 77, "Sixty Second Avenue Inc.", "e"],
["Ada Norton", 6150, 81, 55, "C.H. Lavatory and Sons Company", "e"],
["Leila Vargas", 5344, 515, 9, "Bluth Company Institute", "e"],
["Vernon Stewart", 7117, 598, 44, "Videlectrix Associates", "b"],
["Derek Figueroa", 8547, 41, 70, "ZiffCorp LLC", "d"],
["Ricky Cross", 4972, 562, 41, "United Fried Chicken Incorporated", "d"],
["Manuel Gill", 5209, 326, 67, "Chez Quis PC", "c"],
["Dylan Larson", 5906, 679, 73, "General Services Corporation LLC", "b"],
["Frank Carlson", 155, 528, 79, "Omni Consimer Products Institute", "b"],
["Jerome Fisher", 2041, 842, 93, "Virtucon Limited", "e"],
["Kyle Dawson", 4209, 7, 59, "Mammoth Pictures Corporation", "c"],
["Grace Blair", 2808, 420, 69, "United Fried Chicken Industries", "a"],
["Tony Wilkins", 5107, 309, 59, "Vandelay Industries Foundation", "b"],
["Jacob Carter", 5983, 789, 92, "Vandelay Industries Corporation", "d"],
["Jason Burns", 2779, 421, 68, "Barrytron LLP", "d"],
["Jay Vargas", 4174, 790, 95, "Allied Biscuit Company", "d"],
["Jose Webster", 6438, 373, 47, "Tessier-Ashpool Ltd", "e"],
["Jack Neal", 6170, 23, 100, "Spacely Sprockets Limited", "d"],
["Madge Saunders", 6259, 312, 49, "Sixty Second Avenue Institute", "b"],
["Antonio Saunders", 2660, 349, 9, "Keedsler Motors LLC", "d"],
["Stanley Herrera", 1254, 602, 11, "Demo, inc. Corp.", "a"],
["Cynthia Hopkins", 2118, 963, 27, "Mr. Sparkle Corporation", "c"],
["Alejandro Perkins", 8020, 675, 92, "Mainway Toys Associates", "d"],
["Emma Woods", 3765, 25, 74, "Rouster and Sidewazys PC", "b"],
["Lola Matthews", 7131, 50, 23, "Blammo LLP", "e"],
["Dollie Figueroa", 966, 139, 99, "Input Inc. Inc.", "e"],
["Jordan Ramos", 4857, 764, 51, "Flowers By Irene Ltd", "a"],
["Marcus Bailey", 834, 5, 89, "Bluth Company Ltd", "c"],
["Lester Logan", 5214, 260, 6, "ZiffCorp Industries", "b"],
["Jeff Gardner", 9645, 468, 76, "LexCorp Corp.", "e"],
["Ricky Herrera", 6574, 395, 81, "Mr. Sparkle Incorporated", "b"],
["Bernard Green", 5154, 359, 14, "Globex Corporation Corporation", "a"],
["Ellen Harvey", 63, 51, 62, "Corellian Engineering Corporation Company", "e"],
["Kathryn Mitchell", 141, 234, 11, "Globex Corporation Institute", "a"],
["Adrian Sullivan", 2382, 57, 65, "Osato Chemicals LLP", "a"],
["Willie Saunders", 1120, 902, 19, "Sirius Cybernetics Corporation LLP", "c"],
["Margaret Ortega", 1726, 461, 46, "Zevo Toys Consulting", "e"],
["Lelia Schwartz", 2906, 22, 66, "Klimpys Company", "e"],
["Katherine Reyes", 5020, 218, 2, "Keedsler Motors Company", "e"],
["Alan Padilla", 2696, 691, 31, "Data Systems Ltd", "b"],
["Josephine Wilkins", 8140, 545, 70, "McMahon and Tate Associates", "a"],
["Franklin Riley", 1179, 994, 73, "Roboto Industries Company", "e"],
["Lillian Lee", 5715, 151, 57, "Omni Consimer Products PC", "c"],
["Theresa Payne", 8939, 642, 89, "Burleigh and Stronginthearm Limited", "b"],
["Jeffrey Bridges", 1601, 975, 18, "Sixty Second Avenue Incorporated", "e"],
["Devin Black", 8985, 576, 51, "Thatherton Fuels Limited", "c"],
["Ellen Kim", 6453, 0, 5, "Demo Company Company", "c"],
["Myrtie Jackson", 2240, 129, 94, "Chotchkies Consulting", "b"],
["Lida Crawford", 1706, 433, 24, "Megadodo Publications LLP", "d"],
["Tony Stone", 1463, 321, 55, "Industrial Automation PC", "a"],
["Derek Greer", 255, 949, 92, "LuthorCorp Consulting", "a"],
["Jeremiah McBride", 6754, 422, 2, "Incom Corporation Associates", "d"],
["Lloyd Allison", 9123, 665, 13, "Plow King Limited", "e"],
["Ida Payne", 6, 658, 97, "United Fried Chicken Ltd", "b"],
["Randall Washington", 1736, 271, 18, "Three Waters Ltd", "d"],
["Francis Norris", 2267, 831, 100, "Carrys Candles Incorporated", "c"],
["Ray Gonzales", 9730, 149, 8, "U.S. Robotics and Mechanical Men Corporation", "e"],
["Margaret McCormick", 5844, 946, 86, "Transworld Consortium Company", "c"],
["Christian Robertson", 3419, 624, 5, "Niagular Inc.", "a"],
["Cora Phelps", 1524, 757, 73, "Roboto Industries LLP", "b"],
["Ida Ramsey", 5518, 5, 36, "Acme inc. Corporation", "a"],
["Lydia Sherman", 7064, 246, 23, "Spade and Archer LLC", "c"],
["Elijah Sanders", 241, 739, 83, "Mainway Toys Corp.", "a"],
["Willie Ford", 6611, 682, 41, "QWERTY Logistics Corp.", "e"],
["Susan Phelps", 9222, 326, 30, "Niagular Inc.", "a"],
["Isabella Obrien", 1739, 543, 15, "Taggart Transcontinental Foundation", "e"],
["Alta West", 7408, 477, 0, "Mooby Corp Associates", "d"],
["Adrian Jacobs", 6940, 983, 65, "Spade and Archer Associates", "b"],
["Danny Collins", 4848, 797, 64, "Central Perk Company", "b"],
["Arthur Tucker", 6214, 902, 42, "Sample inc LLP", "d"],
["Larry Griffin", 6805, 987, 13, "Demo, inc. Corporation", "c"],
["Owen Barnett", 322, 73, 66, "Wentworth Industries Company", "c"],
["Raymond McCoy", 7881, 218, 1, "Sombra Corporation Industries", "d"],
["Franklin Wheeler", 8209, 381, 5, "Spade and Archer Foundation", "a"],
["Mamie Wells", 2114, 29, 32, "Ajax Incorporated", "c"],
["Chase Holland", 9956, 843, 79, "Monarch Playing Card Co. Company", "d"],
["Sarah Simon", 9897, 343, 87, "Axis Chemical Co. Ltd", "c"],
["Marguerite Larson", 3621, 917, 3, "Sixty Second Avenue LLP", "e"],
["Cora Turner", 5896, 193, 61, "Minuteman Cafe Associates", "e"],
["Darrell Torres", 6160, 109, 71, "Strickland Propane Limited", "d"],
["Elijah Vasquez", 7592, 138, 32, "Universal Export Corporation", "b"],
["Erik Becker", 9778, 723, 45, "The Legitimate Businessmens Club Industries", "a"],
["Emma Fitzgerald", 3225, 813, 24, "ZiffCorp Ltd", "d"],
["Carolyn Rivera", 5769, 225, 18, "Corellian Engineering Corporation Associates", "a"],
["Viola Mendoza", 5278, 339, 57, "Stay Puft Corporation PC", "e"],
["Vincent Schwartz", 4550, 131, 52, "Widget Corp Incorporated", "d"],
["Michael Marshall", 8894, 778, 81, "Duff Brewing Company Corp.", "b"],
["Adele Roberson", 5220, 638, 68, "Taggart Transcontinental Ltd", "c"],
["Helen Jacobs", 9150, 471, 38, "Taco Grande Industries", "c"],
["Anne Wilkins", 9674, 462, 50, "Tessier-Ashpool Ltd", "d"],
["Floyd Dennis", 1755, 424, 7, "Western Gas & Electric Consulting", "a"],
["Callie Horton", 1732, 86, 33, "Sto Plains Holdings Company", "e"],
["Aiden Wallace", 3897, 162, 28, "U.S. Robotics and Mechanical Men Associates", "d"],
["Lloyd Lucas", 734, 864, 10, "Gadgetron Industries", "e"],
["Joel Grant", 7999, 223, 1, "Allied Biscuit PC", "b"],
["Jeffrey Mendoza", 7445, 252, 20, "Zevo Toys LLC", "d"],
["Mildred Smith", 7260, 865, 0, "Demo, inc. Industries", "e"],
["Elijah Blair", 5313, 377, 34, "QWERTY Logistics Industries", "e"],
["Joe Becker", 8480, 172, 82, "Compuglobalhypermeganet Consulting", "c"],
["Jared Harmon", 8326, 453, 27, "Minuteman Cafe Incorporated", "b"],
["Danny Strickland", 3992, 486, 79, "Minuteman Cafe Company", "a"],
["Sally Cannon", 8327, 900, 27, "LuthorCorp Company", "d"],
["Nora Valdez", 1300, 893, 68, "Tessier-Ashpool Institute", "e"],
["Alan Washington", 5126, 199, 68, "Industrial Automation LLP", "a"],
["Viola Terry", 7271, 979, 42, "BLAND Corporation Corporation", "d"],
["Cynthia Ruiz", 9364, 833, 4, "Videlectrix Consulting", "a"],
["Helena Arnold", 4399, 916, 80, "Leeding Engines Ltd. Company", "d"],
["Sylvia Carroll", 109, 689, 90, "Initech Incorporated", "b"],
["Eric Cox", 2598, 3, 34, "Osato Chemicals Industries", "b"],
["Cordelia Blair", 5569, 175, 6, "Chez Quis Associates", "d"],
["Della Warner", 1713, 141, 3, "Milliways Corp.", "b"],
["Howard Sharp", 132, 225, 52, "Taggart Transcontinental Limited", "a"],
["Owen Adams", 3558, 205, 60, "LuthorCorp Incorporated", "b"],
["Sara Allen", 7008, 453, 27, "Keedsler Motors Ltd", "e"],
["Lillian Jackson", 6591, 64, 28, "Acme inc. LLC", "d"],
["Eleanor Owens", 6550, 431, 3, "Taco Grande Ltd", "d"],
["Virginia Fowler", 858, 170, 26, "Tip Top Cafe Foundation", "a"],
["Bernard Jordan", 9564, 799, 24, "North Central Positronics Incorporated", "b"],
["Estelle Valdez", 5247, 840, 28, "Sixty Second Avenue Industries", "e"],
["Claudia Luna", 4936, 301, 9, "Omni Consimer Products Industries", "d"],
["Jerome Collins", 5491, 335, 66, "Thrift Bank Ltd", "e"],
["Alex Webster", 7320, 873, 18, "Wernham Hogg Ltd", "c"],
["Chester Summers", 5350, 836, 62, "Videlectrix Foundation", "a"],
["Beatrice Hodges", 2366, 355, 3, "Demo Company Associates", "c"],
["Leona Mills", 8609, 602, 88, "Plow King Company", "a"],
["Jennie Nunez", 2751, 439, 85, "Blammo LLP", "a"],
["Pearl Santos", 3636, 902, 97, "Omni Consimer Products Limited", "e"],
["Hannah Waters", 9943, 329, 95, "Barrytron Limited", "b"],
["Lizzie Soto", 1475, 682, 40, "Mr. Sparkle Associates", "d"],
["Lucas Rodriquez", 376, 386, 24, "Mooby Corp Corporation", "e"],
["Dora Anderson", 2719, 807, 8, "Dunder Mifflin Limited", "c"],
["Nicholas Harmon", 2644, 135, 42, "LuthorCorp Limited", "d"],
["Cecelia Carr", 7382, 27, 28, "LexCorp Company", "e"],
["Ian Schwartz", 7242, 751, 64, "Allied Biscuit Corporation", "b"],
["Jane Malone", 2048, 348, 67, "Leeding Engines Ltd. Incorporated", "a"],
["Lilly Parker", 1981, 243, 56, "Charles Townsend Agency Corp.", "a"],
["Joel Nichols", 8230, 687, 20, "Dunder Mifflin Consulting", "e"],
["Alberta Wilson", 9961, 748, 44, "Mainway Toys Inc.", "a"],
["Susan Duncan", 9954, 528, 7, "Colonial Movers Foundation", "d"],
["Martha Sanchez", 4665, 939, 32, "Monks Diner Ltd", "a"],
["Seth Roberson", 634, 837, 13, "123 Warehousing Inc.", "d"],
["Lucy Norman", 9023, 802, 51, "Mammoth Pictures LLC", "a"],
["Harriet Cox", 8017, 784, 64, "Monarch Playing Card Co. Foundation", "d"],
["Juan Hughes", 2118, 952, 81, "Input Inc. Institute", "e"],
["Rachel Fisher", 9710, 32, 41, "Dunder Mifflin PC", "e"],
["Maria Atkins", 2592, 271, 62, "Ajax Industries", "d"],
["Cecilia Ramos", 1130, 801, 39, "Niagular Limited", "d"],
["Harry Moody", 3091, 293, 36, "Wernham Hogg Corp.", "e"],
["Lizzie Hardy", 6378, 870, 69, "Acme Corp Institute", "b"],
["Franklin Grant", 8192, 675, 100, "Minuteman Cafe LLP", "b"],
["Sean Hayes", 3686, 20, 24, "Universal Export Corporation", "d"],
["Ollie Jordan", 8368, 387, 50, "Duff Brewing Company Incorporated", "a"],
["Julian Chapman", 1159, 889, 89, "Globex Corporation Corporation", "d"],
["Lee Adams", 9276, 42, 70, "Ankh-Sto Associates Limited", "c"],
["Lester Webb", 8458, 280, 26, "Virtucon Company", "c"],
["Mary Walton", 3933, 264, 17, "Globo Gym American Corp Institute", "e"],
["Luella Todd", 9631, 396, 24, "Acme Corp Corp.", "b"],
["Clifford Ingram", 3421, 502, 37, "Praxis Corporation LLC", "c"],
["Cornelia Elliott", 2635, 573, 99, "QWERTY Logistics LLC", "b"],
["Effie Boyd", 7980, 291, 4, "Wernham Hogg Ltd", "b"],
["Georgie Nichols", 5733, 900, 95, "Demo, inc. Industries", "d"],
["Susie Gibbs", 1849, 573, 5, "North Central Positronics Incorporated", "a"],
["Phoebe Santos", 9236, 809, 52, "Petrox Oil Company Institute", "a"],
["Catherine Gill", 1573, 139, 11, "Globo Gym American Corp Limited", "d"],
["Emma McCoy", 7864, 680, 6, "Ankh-Sto Associates Ltd", "d"],
["Bill Salazar", 5916, 33, 64, "Spade and Archer Limited", "b"],
["Rosetta Collier", 1942, 749, 58, "Demo, inc. Limited", "a"],
["Johnny Bridges", 3253, 518, 53, "Strickland Propane Corporation", "b"],
["Claudia Jensen", 5546, 246, 72, "Transworld Consortium Inc.", "a"],
["Howard Bishop", 2454, 923, 50, "Wernham Hogg Consulting", "b"],
["Helena Rodriguez", 504, 462, 60, "Demo, inc. Consulting", "c"],
["Jorge Hicks", 1403, 699, 83, "Mr. Sparkle Consulting", "d"],
["Mathilda Parsons", 1756, 501, 47, "QWERTY Logistics Corp.", "c"],
["Martha Mack", 5844, 788, 35, "North Central Positronics LLP", "a"],
["Ella Perez", 6007, 428, 56, "C.H. Lavatory and Sons Company", "b"],
["Mathilda Adams", 5695, 556, 24, "Minuteman Cafe Corp.", "b"],
["Lulu Morris", 3310, 488, 0, "Initrode Company", "c"],
["Alberta Mullins", 6120, 610, 42, "Keedsler Motors LLC", "d"],
["Jose Ryan", 1629, 306, 92, "Tip Top Cafe Corp.", "d"],
["Jonathan Tate", 1790, 720, 44, "Transworld Consortium LLC", "d"],
["Charles Doyle", 6395, 593, 54, "Sonky Rubber Goods Corp.", "b"],
["Genevieve Padilla", 6742, 205, 57, "Ankh-Sto Associates Industries", "e"],
["Abbie Cain", 8214, 137, 70, "Mammoth Pictures Foundation", "d"],
["Connor Brewer", 734, 980, 88, "Widget Corp Ltd", "a"],
["Rachel Beck", 7349, 203, 51, "Central Perk Corp.", "a"],
["Lula George", 5064, 582, 87, "Keedsler Motors Corp.", "d"],
["Ann Cobb", 7716, 721, 70, "Spade and Archer Incorporated", "c"],
["Charlotte Mitchell", 7130, 651, 94, "Taggart Transcontinental Associates", "b"],
["Lawrence Hall", 9326, 294, 91, "Wayne Enterprises Foundation", "d"],
["Lena Alexander", 3535, 645, 35, "Initech Foundation", "a"],
["Sean Morales", 8322, 508, 99, "Megadodo Publications Associates", "b"],
["Andre Hunt", 8260, 438, 16, "Thatherton Fuels Institute", "b"],
["Augusta Berry", 6845, 142, 40, "Mr. Sparkle LLP", "d"],
["Birdie Gill", 2456, 63, 15, "Initech LLP", "a"],
["Adele Burke", 9719, 213, 52, "Barrytron Company", "a"],
["Loretta Walsh", 2042, 941, 96, "Minuteman Cafe LLC", "c"],
["Mattie Vega", 9216, 500, 75, "Minuteman Cafe Foundation", "b"],
["Duane Richards", 6370, 110, 80, "Kumatsu Motors Industries", "c"],
["Harvey Henry", 8655, 758, 9, "LexCorp LLC", "a"],
["Martha Harper", 7046, 605, 0, "LexCorp Company", "d"],
["Lora Hanson", 7974, 444, 62, "LexCorp Consulting", "b"],
["Myrtie Jenkins", 2152, 597, 72, "Water and Power Inc.", "c"],
["Annie McGuire", 7824, 826, 7, "Extensive Enterprise Inc.", "e"],
["Stanley Fields", 3068, 849, 22, "Industrial Automation Institute", "d"],
["Essie Potter", 5588, 254, 94, "Demo Company Associates", "c"],
["Gilbert Santos", 2207, 330, 77, "QWERTY Logistics Company", "a"],
["John Stephens", 5735, 229, 8, "Initrode Institute", "e"],
["Jeffrey Martinez", 1469, 713, 72, "Widget Corp Institute", "b"],
["Marion Taylor", 6881, 52, 43, "Stay Puft Corporation Corporation", "c"],
["Augusta Robertson", 3782, 508, 61, "Charles Townsend Agency Incorporated", "e"],
["Alejandro Perry", 9218, 751, 91, "Tip Top Cafe Corporation", "a"],
["Ethan Collier", 3955, 312, 49, "Mooby Corp Company", "a"],
["Jayden Lawrence", 8602, 96, 96, "Carrys Candles Foundation", "d"],
["Fanny Holland", 2436, 367, 4, "Moes Tavern Corporation", "e"],
["Charlie Jenkins", 5118, 528, 12, "Videlectrix LLP", "e"],
["Louis Richards", 9786, 749, 90, "Initech Institute", "c"],
["Adeline Banks", 5453, 459, 53, "Thrift Bank Ltd", "e"],
["Lawrence Oliver", 3546, 725, 43, "Megadodo Publications Foundation", "b"],
["Leila Nunez", 6198, 502, 35, "Industrial Automation Incorporated", "b"],
["Callie French", 2873, 664, 14, "Megadodo Publications Associates", "e"],
["Ola Wallace", 2594, 777, 37, "Barrytron Associates", "a"],
["Clyde Rodriquez", 140, 456, 51, "Gizmonic Institute LLP", "e"],
["Jordan Edwards", 4240, 44, 47, "Zevo Toys Corporation", "b"],
["Anne Mitchell", 2587, 142, 72, "Moes Tavern Corp.", "e"],
["Dora McKinney", 9569, 707, 83, "The Krusty Krab LLP", "e"],
["Joshua Gutierrez", 4590, 214, 4, "Moes Tavern Incorporated", "b"],
["Lou Powell", 4710, 113, 10, "Charles Townsend Agency Incorporated", "d"],
["Leroy Bailey", 6635, 907, 91, "Plow King Consulting", "e"],
["Hilda McKinney", 2070, 692, 76, "Wentworth Industries Limited", "b"],
["Nicholas Woods", 5219, 774, 67, "Sirius Cybernetics Corporation Corp.", "e"],
["Eula Moore", 663, 844, 1, "Wernham Hogg Consulting", "c"],
["Maude Mendoza", 5884, 903, 8, "Zevo Toys Corporation", "e"],
["Bessie McDonald", 2241, 346, 66, "General Products Corp.", "a"],
["Dora Padilla", 5212, 218, 79, "Gadgetron Company", "e"],
["Josephine Vaughn", 592, 677, 39, "Thrift Bank LLP", "e"],
["Clyde Davis", 5293, 576, 71, "Sombra Corporation Inc.", "a"],
["Leonard Cohen", 6331, 297, 19, "Sombra Corporation PC", "c"],
["Lulu Harmon", 5014, 530, 8, "Big Kahuna Burger Limited", "e"],
["Stanley Nelson", 7138, 937, 1, "General Products Institute", "b"],
["Lee Webster", 5559, 596, 58, "Stay Puft Corporation LLP", "d"],
["Leonard Rodgers", 534, 692, 81, "Sonky Rubber Goods Limited", "a"],
["Wesley Holmes", 1679, 165, 91, "Mooby Corp Incorporated", "a"],
["David Becker", 6093, 464, 53, "Sixty Second Avenue Incorporated", "c"],
["Craig Schneider", 9981, 63, 62, "Universal Export Foundation", "c"],
["Scott McLaughlin", 9511, 354, 28, "Taco Grande LLP", "d"],
["Mary Garner", 4402, 745, 85, "Transworld Consortium Associates", "c"],
["Josie Keller", 3849, 855, 84, "Flowers By Irene Ltd", "a"],
["Eugenia Hudson", 9024, 354, 32, "Initrode Company", "e"],
["Gregory Reeves", 2175, 668, 94, "SpringShield Consulting", "d"],
["Jared Collier", 3883, 88, 72, "Carrys Candles Consulting", "e"],
["Leon Kennedy", 5068, 264, 7, "Mainway Toys Inc.", "a"],
["Alvin Allison", 6304, 60, 49, "BLAND Corporation Foundation", "c"],
["Isaac Perez", 1996, 985, 37, "Chotchkies Foundation", "c"],
["Richard Matthews", 7189, 986, 5, "Gadgetron PC", "d"],
["Clayton Rivera", 9780, 831, 34, "Acme inc. Inc.", "a"],
["Eva George", 9086, 197, 93, "Megadodo Publications LLC", "a"],
["Melvin Curry", 864, 391, 30, "Allied Biscuit LLC", "d"],
["Norman Alexander", 7083, 955, 20, "Tessier-Ashpool LLC", "d"],
["Gabriel Burgess", 1012, 490, 28, "Chasers Inc.", "c"],
["Nelle Henderson", 9299, 885, 77, "Wayne Enterprises Corporation", "e"],
["Mabel Saunders", 7400, 257, 29, "Demo Company Associates", "d"],
["Hulda Gutierrez", 3323, 560, 84, "Galaxy Corp Inc.", "b"],
["Ivan McKinney", 3169, 447, 44, "Gadgetron Incorporated", "e"],
["Viola Boyd", 8537, 49, 20, "U.S. Robotics and Mechanical Men Institute", "e"],
["Cordelia Knight", 7163, 896, 67, "Big Kahuna Burger Industries", "e"],
["Miguel Shelton", 5515, 696, 52, "Zevo Toys Institute", "e"],
["Sadie Williams", 4650, 474, 13, "Three Waters Limited", "a"],
["Nancy Glover", 2109, 775, 8, "Acme Corp Corporation", "e"],
["Billy Graham", 3672, 894, 70, "Burleigh and Stronginthearm Limited", "e"],
["Philip Brewer", 9289, 847, 94, "Sample Company Inc.", "e"],
["Hunter Davidson", 4223, 281, 99, "Globo Gym American Corp Limited", "b"],
["Curtis Leonard", 6449, 479, 27, "Smith and Co. Incorporated", "b"],
["Larry Alvarado", 139, 312, 65, "McMahon and Tate Corp.", "b"],
["Alejandro Hill", 9158, 689, 12, "Spacely Sprockets Corp.", "d"],
["Joel Nelson", 5908, 637, 67, "Acme inc. Incorporated", "a"],
["Andrew Moody", 296, 433, 23, "Thatherton Fuels Ltd", "d"],
["Martha Welch", 7500, 9, 67, "Mooby Corp Ltd", "e"],
["Caleb Gray", 3369, 56, 93, "Allied Biscuit PC", "d"],
["Tyler Weber", 6409, 393, 25, "Globo-Chem Incorporated", "a"],
["Leroy Rhodes", 7556, 140, 44, "Foo Bars LLC", "c"],
["Clara Butler", 2700, 969, 6, "Tip Top Cafe Corp.", "d"],
["Lois Cruz", 705, 804, 47, "General Services Corporation Company", "b"],
["Billy Gross", 2750, 738, 65, "Initech Corporation", "d"],
["Steve Thomas", 3304, 944, 8, "Monarch Playing Card Co. Limited", "a"],
["Lela Horton", 9448, 664, 83, "Big Kahuna Burger Inc.", "e"],
["Myra Smith", 3710, 933, 63, "QWERTY Logistics Corporation", "c"],
["Lawrence Osborne", 7490, 28, 32, "Allied Biscuit Institute", "e"],
["Ryan Webb", 4973, 245, 47, "Charles Townsend Agency LLP", "c"],
["Katherine Holmes", 4936, 375, 58, "Demo Company PC", "c"],
["Phillip Harvey", 2441, 302, 98, "Mammoth Pictures LLP", "c"],
["Seth Davis", 2441, 156, 70, "Powell Motors Associates", "e"],
["Lydia Keller", 6008, 471, 92, "Charles Townsend Agency Corp.", "e"],
["Mayme Curry", 1056, 318, 33, "QWERTY Logistics Consulting", "a"],
["Andrew McKenzie", 452, 299, 26, "Western Gas & Electric LLC", "c"],
["Carlos Manning", 6801, 927, 77, "Niagular Limited", "d"],
["Alberta Bryan", 332, 671, 6, "Monarch Playing Card Co. Institute", "b"],
["Brandon Jimenez", 7699, 132, 37, "Burleigh and Stronginthearm Inc.", "c"],
["Mollie Torres", 2123, 910, 0, "Roboto Industries Corporation", "c"],
["Jennie Rios", 1079, 44, 59, "The Frying Dutchman Corporation", "d"],
["Lilly Floyd", 9735, 416, 67, "Mammoth Pictures Incorporated", "a"],
["Cecelia Estrada", 4205, 83, 31, "Ajax Ltd", "b"],
["Elva Perkins", 6935, 103, 3, "Colonial Movers Institute", "c"],
["Francisco Martinez", 6813, 417, 0, "Tip Top Cafe Corp.", "c"],
["Bradley Manning", 9926, 978, 33, "Dunder Mifflin Limited", "b"],
["Keith Russell", 8178, 588, 30, "North Central Positronics Institute", "a"],
["Steve Patton", 1650, 735, 65, "Burleigh and Stronginthearm Ltd", "b"],
["Alfred Klein", 5070, 970, 30, "Megadodo Publications Industries", "b"],
["Ophelia Hernandez", 8634, 17, 99, "SpringShield Incorporated", "d"],
["Helen Ferguson", 3143, 441, 94, "Keedsler Motors LLC", "e"],
["Ivan Phillips", 4264, 45, 36, "Spacely Sprockets Foundation", "e"],
["William Butler", 9005, 31, 2, "Videlectrix Industries", "c"],
["Landon Holland", 9604, 737, 23, "Globo Gym American Corp Corp.", "b"],
["Alan Bates", 8967, 941, 39, "Spade and Archer Industries", "a"],
["Willie Gonzales", 1195, 981, 67, "Gizmonic Institute Limited", "d"],
["Delia Howell", 2676, 62, 19, "Vandelay Industries PC", "e"],
["Gordon Cohen", 3374, 318, 64, "Megadodo Publications Industries", "d"],
["Billy Carlson", 4318, 825, 35, "Tip Top Cafe LLP", "a"],
["Dylan Wallace", 5594, 825, 31, "Moes Tavern Associates", "d"],
["Marie Becker", 2624, 85, 9, "U.S. Robotics and Mechanical Men Company", "c"],
["Sophia Ramirez", 8514, 92, 69, "Allied Biscuit Inc.", "e"],
["Clarence Medina", 7249, 9, 51, "Incom Corporation LLC", "b"],
["Micheal Andrews", 402, 726, 48, "BLAND Corporation Corp.", "e"],
["Francis Lawson", 4130, 628, 36, "SpringShield LLC", "e"],
["Howard Wolfe", 6373, 694, 5, "The New Firm Inc.", "c"],
["Derek Webb", 1744, 751, 48, "Chotchkies Incorporated", "d"],
["Jennie Bates", 8751, 282, 47, "Universal Export LLP", "b"],
["Gordon Stone", 1801, 512, 59, "123 Warehousing Institute", "d"],
["Alice Lucas", 1538, 995, 40, "Chotchkies Company", "b"],
["Ronald Watkins", 3779, 615, 83, "123 Warehousing PC", "a"],
["Cole Snyder", 4419, 21, 6, "Acme inc. LLP", "a"],
["Jonathan Glover", 7352, 479, 43, "Monarch Playing Card Co. Ltd", "c"],
["Tom Walton", 9929, 586, 6, "Roboto Industries Inc.", "b"],
["Gregory Swanson", 8434, 175, 93, "Osato Chemicals Incorporated", "e"],
["Ricky Medina", 4030, 265, 51, "Gringotts Corp.", "a"],
["Louise Bass", 3717, 78, 12, "Petrox Oil Company LLC", "c"],
["Lydia Hall", 6784, 236, 80, "Tip Top Cafe Institute", "c"],
["Harriet Kelley", 6084, 240, 35, "The New Firm PC", "a"],
["Mike Webster", 3689, 181, 55, "Big T Burgers and Fries Corp.", "e"],
["Tony Logan", 4983, 278, 80, "Rouster and Sidewazys Inc.", "a"],
["Thomas Neal", 4162, 144, 41, "Cogswell Cogs LLC", "b"],
["Trevor Woods", 4853, 446, 11, "Krustyco LLC", "c"],
["Marian Roberts", 4575, 862, 29, "ABC Telecom Industries", "e"],
["John Cook", 2162, 918, 96, "Krustyco Ltd", "c"],
["Brandon Cruz", 8420, 989, 62, "Wernham Hogg Limited", "c"],
["Luella Dawson", 3507, 746, 55, "BLAND Corporation Foundation", "e"],
["Francis Campbell", 2779, 62, 18, "Duff Brewing Company PC", "b"],
["Allie Baker", 3900, 509, 89, "Transworld Consortium Limited", "e"],
["Lilly Carlson", 2134, 200, 28, "Sirius Cybernetics Corporation Foundation", "e"],
["Herbert Lloyd", 297, 170, 49, "Monarch Playing Card Co. LLC", "c"],
["Derek Taylor", 7515, 466, 8, "Monks Diner Industries", "c"],
["Emma Stokes", 2635, 331, 2, "Taggart Transcontinental Company", "a"],
["Olga Brooks", 6068, 691, 43, "Mammoth Pictures Incorporated", "c"],
["Richard Holland", 8063, 128, 0, "Charles Townsend Agency Consulting", "e"],
["Beulah Warren", 5016, 532, 76, "Water and Power Industries", "a"],
["Christian Torres", 2642, 896, 10, "Gadgetron Company", "e"],
["Chris Goodwin", 2333, 213, 7, "Chez Quis Corporation", "e"],
["Gavin Payne", 5560, 888, 26, "Minuteman Cafe Ltd", "d"],
["Dora Ross", 8907, 668, 44, "LexCorp Foundation", "a"],
["Todd Russell", 7421, 144, 67, "Rouster and Sidewazys Consulting", "b"],
["Bessie Abbott", 8443, 101, 30, "United Fried Chicken Incorporated", "b"],
["Gilbert Chambers", 6558, 70, 44, "Leeding Engines Ltd. LLP", "a"],
["Arthur Woods", 9069, 406, 56, "Western Gas & Electric Associates", "a"],
["Addie Beck", 9497, 653, 38, "Acme inc. Company", "a"],
["Alice Park", 4466, 438, 74, "Initrode LLP", "b"],
["Nora Massey", 2017, 423, 96, "Thrift Bank Associates", "b"],
["William Sharp", 9425, 248, 66, "Klimpys Associates", "c"],
["Lelia Erickson", 1452, 943, 34, "Charles Townsend Agency Consulting", "d"],
["Jeffrey Greer", 5608, 510, 71, "QWERTY Logistics Ltd", "a"],
["Jennie Rogers", 1160, 275, 34, "Monks Diner Consulting", "c"],
["Leah Zimmerman", 6470, 283, 21, "North Central Positronics Inc.", "d"],
["Ethan Sherman", 886, 225, 29, "Globo-Chem Limited", "a"],
["Lucas Nash", 8532, 877, 5, "Flowers By Irene LLP", "e"],
["Dean Blair", 7416, 818, 85, "QWERTY Logistics Industries", "b"],
["Russell Kelly", 9168, 220, 55, "Acme inc. Institute", "b"],
["Laura Carter", 2028, 89, 48, "Universal Export LLP", "b"],
["Norman Spencer", 9021, 361, 64, "Mooby Corp LLC", "a"],
["Gabriel Nunez", 3405, 164, 98, "Chotchkies Corp.", "d"],
["Nannie Pope", 2915, 297, 27, "Taggart Transcontinental Company", "c"],
["Howard Higgins", 7773, 167, 63, "ZiffCorp Inc.", "d"],
["Ina Hall", 8109, 785, 34, "Water and Power LLC", "e"],
["Elnora Sanchez", 116, 669, 41, "Initrode Corp.", "a"],
["Jon Rice", 6668, 318, 31, "Monarch Playing Card Co. Associates", "b"],
["Annie Copeland", 6018, 708, 18, "Bluth Company Corp.", "b"],
["Juan Logan", 2603, 94, 6, "Sirius Cybernetics Corporation Corp.", "a"],
["Kevin Ferguson", 2569, 654, 40, "QWERTY Logistics Corporation", "a"],
["Tyler Diaz", 975, 58, 43, "Initech Inc.", "b"],
["Joshua Clayton", 807, 440, 42, "Foo Bars Inc.", "a"],
["Gordon Shelton", 6079, 497, 6, "Spade and Archer Associates", "e"],
["Mary Santiago", 2715, 129, 78, "Blammo Company", "c"],
["Nell Brady", 6422, 67, 87, "Foo Bars Foundation", "d"],
["Olga Francis", 125, 677, 50, "LexCorp PC", "d"],
["Maria Cunningham", 3288, 610, 45, "Cogswell Cogs LLP", "c"],
["Lilly White", 2270, 509, 90, "Wernham Hogg Incorporated", "d"],
["Charles Wilkerson", 4078, 625, 0, "Sirius Cybernetics Corporation Institute", "d"],
["Belle Hunt", 8140, 241, 12, "Spacely Sprockets Ltd", "a"],
["Lena Richards", 6229, 692, 37, "Western Gas & Electric LLP", "e"],
["Jesse Miles", 7615, 669, 23, "Big T Burgers and Fries Limited", "a"],
["Josie Garrett", 9291, 832, 63, "Kumatsu Motors Limited", "d"],
["Earl Becker", 2703, 568, 73, "Bluth Company Inc.", "d"],
["Katharine Caldwell", 6366, 687, 67, "Nordyne Defense Dynamics Consulting", "c"],
["Louisa Thompson", 5842, 410, 74, "Gizmonic Institute Limited", "b"],
["Peter Curry", 7131, 233, 7, "Duff Brewing Company Industries", "e"],
["Frank Luna", 428, 356, 36, "Gizmonic Institute Consulting", "e"],
["Frank Harrison", 8317, 410, 32, "Primatech Consulting", "c"],
["Cordelia Chapman", 4945, 414, 64, "Keedsler Motors Incorporated", "c"],
["Jordan Lynch", 7429, 952, 14, "Gadgetron Incorporated", "b"],
["Adrian Fletcher", 4426, 486, 81, "Virtucon Corp.", "c"],
["Jeremy Francis", 9638, 433, 91, "Demo, inc. Corporation", "c"],
["Eugene McCormick", 5194, 17, 15, "LexCorp Corporation", "e"],
["Lenora Lindsey", 9161, 578, 66, "SpringShield Inc.", "d"],
["Viola Sherman", 4182, 769, 64, "Plow King Industries", "e"],
["Cody Mills", 5558, 713, 98, "Colonial Movers LLC", "a"],
["Madge Wheeler", 8882, 566, 81, "St. Anky Beer LLP", "d"],
["Randall Paul", 5692, 161, 56, "Powell Motors Associates", "a"],
["Lenora Spencer", 1666, 926, 78, "Western Gas & Electric Foundation", "e"],
["Randall George", 1618, 493, 67, "Monks Diner Corp.", "d"],
["Lora Caldwell", 4602, 914, 48, "General Services Corporation Corp.", "b"],
["Isaac Hines", 2031, 946, 23, "Axis Chemical Co. Consulting", "c"],
["Evan McLaughlin", 8656, 504, 23, "Acme Corp Corporation", "c"],
["Stanley Barton", 2702, 782, 40, "Kumatsu Motors Associates", "c"],
["Charlotte Carson", 75, 309, 45, "Allied Biscuit Inc.", "e"],
["Katharine Doyle", 3672, 666, 44, "Strickland Propane Corporation", "e"],
["Harriett Cohen", 4136, 845, 89, "Corellian Engineering Corporation Incorporated", "e"],
["Olga Sims", 9898, 390, 97, "Mainway Toys LLC", "e"],
["Edith Romero", 7515, 225, 44, "Sirius Cybernetics Corporation Corporation", "b"],
["Bruce Patrick", 802, 674, 2, "Blammo Associates", "e"],
["Cynthia Phelps", 7044, 222, 13, "Sirius Cybernetics Corporation Foundation", "b"],
["Gordon Norton", 8747, 622, 27, "Petrox Oil Company Associates", "c"],
["Maurice Bryant", 301, 663, 95, "Moes Tavern Corp.", "e"],
["Mittie Clayton", 6608, 495, 60, "Chotchkies Inc.", "e"],
["James Boyd", 21, 674, 33, "U.S. Robotics and Mechanical Men Foundation", "c"],
["Leila Moody", 6601, 17, 43, "Klimpys Incorporated", "d"],
["Jose Morgan", 316, 152, 58, "Input Inc. Industries", "c"],
["Aaron Bridges", 2353, 751, 91, "Spacely Sprockets Corporation", "e"],
["Della Casey", 1803, 362, 62, "Taco Grande Corp.", "b"],
["Nina Blair", 9549, 675, 54, "Chez Quis Limited", "d"],
["Maude Conner", 9298, 577, 11, "Widget Corp Corporation", "d"],
["Edwin Brady", 9824, 846, 43, "Foo Bars Ltd", "d"],
["Blake Wilkins", 1577, 209, 66, "Universal Export Foundation", "e"],
["Lelia Estrada", 207, 962, 47, "Virtucon Corporation", "e"],
["Scott Keller", 9340, 888, 37, "Duff Brewing Company Corporation", "c"],
["Ethan Roy", 4140, 797, 77, "Osato Chemicals Company", "c"],
["Micheal Gilbert", 5258, 270, 9, "Data Systems Ltd", "e"],
["Sylvia Green", 5774, 281, 90, "Sixty Second Avenue Corporation", "b"],
["Gertrude Armstrong", 2124, 442, 51, "BLAND Corporation PC", "e"],
["Olivia Snyder", 1950, 671, 78, "Sombra Corporation Consulting", "e"],
["Howard Beck", 4278, 522, 51, "Sombra Corporation PC", "d"],
["Floyd Bowman", 4490, 195, 83, "Sample Company Corporation", "a"],
["Mabelle Thomas", 7888, 198, 58, "Primatech Limited", "e"],
["Leona Maxwell", 8853, 626, 58, "Tip Top Cafe Ltd", "d"],
["Glen Oliver", 533, 59, 90, "Roxxon Company", "d"],
["Annie Roberson", 767, 383, 28, "Input Inc. Incorporated", "b"],
["Walter Pearson", 3616, 611, 53, "Gizmonic Institute LLP", "c"],
["Ray Miller", 6502, 436, 48, "Minuteman Cafe Limited", "a"],
["Beulah Fisher", 8167, 242, 35, "Galaxy Corp Corp.", "e"],
["Sylvia Horton", 1715, 420, 100, "Colonial Movers Ltd", "a"],
["Carolyn Sharp", 834, 471, 66, "Sample inc Limited", "a"],
["Seth Swanson", 1110, 948, 41, "Input Inc. Foundation", "d"],
["Kyle Becker", 8242, 884, 67, "Transworld Consortium Consulting", "c"],
["Matthew Banks", 1303, 598, 78, "ZiffCorp Consulting", "c"],
["Keith Mitchell", 1874, 798, 49, "Universal Export LLP", "a"],
["Charles Rowe", 9238, 233, 90, "St. Anky Beer Inc.", "a"],
["Mattie Taylor", 9076, 805, 64, "Big Kahuna Burger Company", "e"],
["Eugene Pope", 8416, 717, 87, "Big Kahuna Burger Associates", "d"],
["Ina Walters", 8650, 623, 34, "Mammoth Pictures Corporation", "b"],
["Harold Carr", 7061, 627, 41, "ABC Telecom LLP", "d"],
["Amy Murray", 6928, 487, 70, "Spade and Archer Corporation", "e"],
["Larry Hicks", 5248, 283, 0, "Water and Power Foundation", "c"],
["Rosie Hogan", 1801, 171, 16, "The Legitimate Businessmens Club Foundation", "c"],
["Albert Carpenter", 8891, 753, 99, "Zevo Toys Ltd", "d"],
["Sylvia Ward", 9267, 299, 74, "The New Firm Corp.", "b"],
["Jerry Carson", 8624, 631, 25, "Colonial Movers Consulting", "a"],
["Inez Austin", 305, 750, 65, "SpringShield LLP", "e"],
["Emma Walton", 3534, 819, 73, "Osato Chemicals Associates", "c"],
["Sean Davidson", 5970, 126, 6, "Duff Brewing Company Company", "d"],
["Alvin Schmidt", 9288, 31, 0, "Chez Quis Consulting", "a"],
["Joel Jensen", 9218, 549, 88, "Mooby Corp Corp.", "c"],
["Sam Marshall", 2830, 657, 30, "Mr. Sparkle LLP", "b"],
["William Reeves", 1659, 163, 89, "Omni Consimer Products Limited", "b"],
["Addie Zimmerman", 4935, 109, 75, "Charles Townsend Agency Consulting", "d"],
["Susie Atkins", 3829, 615, 34, "Barrytron Industries", "e"],
["Vernon Hudson", 217, 332, 67, "Sample Company Corporation", "e"],
["Sally Morton", 9130, 88, 52, "Axis Chemical Co. Foundation", "d"],
["Georgie Dennis", 6525, 663, 74, "Ankh-Sto Associates Ltd", "b"],
["Etta Smith", 8761, 519, 95, "Flowers By Irene LLC", "a"],
["Arthur Berry", 7710, 737, 47, "Globex Corporation Ltd", "b"],
["Derrick Fowler", 3538, 662, 86, "QWERTY Logistics Incorporated", "e"],
["Belle Young", 5089, 398, 20, "Praxis Corporation Company", "b"],
["Alma Higgins", 6698, 929, 67, "Moes Tavern Foundation", "d"],
["Elnora Becker", 8823, 358, 94, "QWERTY Logistics LLP", "a"],
["Seth Mendez", 1761, 171, 78, "The Legitimate Businessmens Club PC", "c"],
["Shane Wagner", 9708, 109, 78, "Globo-Chem Associates", "e"],
["Pauline Horton", 3177, 995, 39, "Tip Top Cafe Foundation", "b"],
["Howard Lawson", 149, 802, 55, "Thatherton Fuels Associates", "e"],
["Ina White", 5647, 991, 2, "Omni Consimer Products Consulting", "e"],
["Rosie Cooper", 8542, 186, 49, "Three Waters Inc.", "e"],
["Mitchell Ball", 646, 518, 52, "Tessier-Ashpool Incorporated", "e"],
["Warren Patrick", 8913, 880, 84, "Dunder Mifflin Inc.", "b"],
["Ernest Bell", 6517, 559, 96, "Duff Brewing Company Consulting", "c"],
["Bobby Dennis", 7473, 317, 40, "Mr. Sparkle Incorporated", "d"],
["Lydia Poole", 7677, 540, 1, "Flowers By Irene Inc.", "c"],
["Donald Watts", 4430, 210, 35, "Sto Plains Holdings Consulting", "c"],
["Harold Underwood", 697, 107, 85, "Initech Consulting", "a"],
["Joel Frank", 8171, 274, 5, "Powell Motors Corporation", "c"],
["Annie Erickson", 7378, 43, 43, "Sonky Rubber Goods Inc.", "e"],
["Walter Holt", 3935, 82, 62, "Klimpys Consulting", "c"],
["Francisco Griffith", 1309, 868, 69, "Globex Corporation Associates", "b"],
["Mattie Blair", 7466, 320, 28, "Praxis Corporation Company", "d"],
["Olga May", 3863, 57, 82, "Sto Plains Holdings LLP", "e"],
["Belle Newman", 7235, 10, 28, "Cogswell Cogs Foundation", "d"],
["Christian Daniel", 7598, 751, 6, "Cogswell Cogs LLC", "b"],
["Luke Gomez", 1266, 71, 23, "Virtucon PC", "d"],
["Maggie Benson", 3723, 496, 30, "Flowers By Irene Industries", "c"],
["Gene Williamson", 6712, 555, 67, "Chotchkies Company", "e"],
["Ida Ortega", 8463, 266, 3, "Sixty Second Avenue Corporation", "e"],
["Jim Moss", 1022, 270, 27, "Galaxy Corp Industries", "a"],
["Craig McLaughlin", 4062, 465, 38, "Blammo LLC", "b"],
["Marguerite Bridges", 4690, 955, 64, "Moes Tavern Limited", "d"],
["Carlos Dawson", 5169, 610, 57, "Wernham Hogg LLP", "c"],
["Ada Simpson", 2909, 709, 30, "Input Inc. Corporation", "a"],
["Lillie Sharp", 3019, 921, 42, "The Krusty Krab LLP", "d"],
["Jack Farmer", 3267, 301, 62, "Kumatsu Motors Incorporated", "e"],
["Eugene McGuire", 8940, 473, 77, "Thrift Bank PC", "b"],
["Polly Reed", 4007, 319, 92, "QWERTY Logistics Ltd", "a"],
["Chester Sharp", 9, 697, 18, "Thatherton Fuels LLC", "d"],
["Mollie Shelton", 1545, 767, 24, "Ankh-Sto Associates Associates", "c"],
["Fannie Jensen", 6692, 135, 67, "Barrytron PC", "c"],
["Tyler Cain", 8177, 311, 86, "Burleigh and Stronginthearm Limited", "e"],
["Olivia Brewer", 1767, 1000, 33, "Virtucon Foundation", "a"],
["Louise Cross", 2352, 921, 11, "Rouster and Sidewazys Incorporated", "c"],
["Raymond Ortega", 9840, 815, 37, "Universal Export Limited", "e"],
["Warren Carroll", 9225, 553, 33, "Initech Foundation", "e"],
["Russell Christensen", 7578, 648, 0, "Dunder Mifflin Corporation", "b"],
["Johanna Herrera", 7113, 480, 49, "Big Kahuna Burger Industries", "d"],
["Miguel Leonard", 5688, 990, 15, "Corellian Engineering Corporation PC", "a"],
["Lettie Ray", 6349, 327, 3, "Demo Company Institute", "d"],
["Rachel Gray", 5404, 105, 24, "Tessier-Ashpool LLC", "e"],
["Edna Reed", 9501, 108, 24, "Gadgetron Inc.", "e"],
["Ada Rodgers", 2650, 488, 92, "C.H. Lavatory and Sons Industries", "a"],
["Roxie Bryant", 6804, 449, 21, "Compuglobalhypermeganet Foundation", "c"],
["Dorothy Gibson", 2301, 557, 78, "Milliways Corporation", "d"],
["Landon Aguilar", 5435, 614, 40, "Galaxy Corp Corporation", "c"],
["Earl Frazier", 7799, 135, 76, "Wentworth Industries Incorporated", "a"],
["Garrett Lindsey", 282, 44, 8, "Blammo Industries", "d"],
["Isabel Blair", 5438, 855, 0, "QWERTY Logistics Corp.", "b"],
["Ola Peterson", 8314, 793, 20, "Taggart Transcontinental Corp.", "d"],
["Augusta Payne", 9643, 985, 72, "Acme Corp Associates", "d"],
["Nell Hunt", 2884, 38, 75, "Osato Chemicals Incorporated", "d"],
["Christina Aguilar", 7908, 902, 64, "Megadodo Publications Foundation", "e"],
["Sam Byrd", 7410, 951, 26, "Mainway Toys LLP", "b"],
["Annie Kim", 6689, 636, 59, "Kumatsu Motors Incorporated", "d"],
["Michael Singleton", 9252, 931, 78, "Corellian Engineering Corporation Institute", "c"],
["Steve Jordan", 7177, 210, 59, "Chasers LLC", "b"],
["Robert Lawrence", 7679, 842, 65, "Universal Export Company", "c"],
["Olivia Townsend", 253, 461, 55, "Input Inc. Industries", "b"],
["Luis Colon", 7506, 415, 56, "General Products Limited", "c"],
["Mathilda Arnold", 9546, 480, 83, "Thrift Bank Limited", "d"],
["Tommy Love", 2749, 61, 7, "LexCorp Limited", "d"],
["Edith Wolfe", 1850, 834, 86, "Megadodo Publications Corp.", "c"],
["Harry Reid", 60, 291, 82, "Wernham Hogg Industries", "c"],
["Troy Kelly", 3916, 552, 61, "Powell Motors Institute", "d"],
["Cornelia Waters", 1104, 950, 72, "United Fried Chicken Corp.", "a"],
["Barbara Simmons", 3375, 69, 31, "Tessier-Ashpool Corporation", "b"],
["Maria Moreno", 352, 955, 49, "Kumatsu Motors Incorporated", "c"],
["Katherine Keller", 7484, 225, 65, "Barrytron Incorporated", "b"],
["Craig Vaughn", 9857, 387, 41, "Mooby Corp Limited", "d"],
["Carolyn Burke", 8916, 375, 61, "St. Anky Beer Foundation", "a"],
["Sara Santiago", 6476, 257, 88, "123 Warehousing LLC", "b"],
["Effie Martinez", 4210, 358, 72, "Nordyne Defense Dynamics PC", "e"],
["Stella Wood", 5854, 38, 19, "Universal Export Institute", "d"],
["Adele Sandoval", 8529, 479, 65, "Galaxy Corp Industries", "b"],
["Charlotte Austin", 9456, 653, 61, "Western Gas & Electric LLC", "e"],
["Louis Phillips", 4101, 802, 29, "Rouster and Sidewazys PC", "d"],
["Bobby Ortega", 9460, 89, 79, "Monks Diner Company", "c"],
["Todd Strickland", 5047, 682, 15, "The Legitimate Businessmens Club Industries", "e"],
["Margaret Waters", 1439, 910, 99, "McMahon and Tate Incorporated", "e"],
["Glen Woods", 8577, 35, 2, "The Legitimate Businessmens Club Consulting", "d"],
["Ann Wise", 704, 933, 34, "Widget Corp Limited", "e"],
["Harriett Phelps", 4267, 994, 68, "Roxxon PC", "d"],
["Angel Patrick", 2419, 299, 39, "Globex Corporation Institute", "b"],
["Franklin McDonald", 7432, 426, 19, "Roxxon Inc.", "a"],
["Sara Patrick", 6237, 358, 98, "Chasers LLC", "d"],
["Helen Newton", 6490, 693, 87, "Kumatsu Motors Inc.", "d"],
["Allie Watts", 4703, 361, 74, "Klimpys Industries", "b"],
["Brett Glover", 9601, 334, 83, "Universal Export Company", "d"],
["Jessie Tucker", 3308, 369, 79, "Roboto Industries Limited", "d"],
["Ralph Riley", 9804, 370, 57, "Megadodo Publications PC", "e"],
["Estella Jefferson", 9243, 100, 24, "Chasers PC", "b"],
["Pearl McBride", 5315, 916, 49, "Transworld Consortium LLP", "a"],
["Gary Mathis", 6466, 54, 20, "Mammoth Pictures Inc.", "b"],
["Samuel Holt", 6686, 822, 11, "Gringotts Limited", "a"],
["Fannie Lyons", 4764, 654, 71, "Tip Top Cafe Institute", "e"],
["Scott Simmons", 1883, 382, 95, "Wernham Hogg Industries", "e"],
["Amy Sandoval", 9353, 839, 68, "Mooby Corp Industries", "a"],
["Daisy Lowe", 4907, 367, 19, "Data Systems Associates", "e"],
["Douglas Bryant", 1596, 595, 67, "Barrytron Company", "e"],
["Ernest Martin", 5542, 165, 13, "Foo Bars Industries", "e"],
["Dominic Harmon", 6107, 205, 50, "Demo Company LLP", "d"],
["Nellie Walsh", 58, 601, 87, "Chez Quis Institute", "d"],
["Cora Jordan", 8269, 407, 96, "Initech Limited", "b"],
["Jack Patton", 6061, 465, 97, "Sonky Rubber Goods Ltd", "b"],
["Owen Ramos", 6894, 725, 36, "Taggart Transcontinental Institute", "d"],
["Laura Lane", 5045, 247, 77, "Gadgetron Corp.", "b"],
["Polly Stevenson", 9112, 247, 41, "Thrift Bank Inc.", "e"],
["Frederick Guerrero", 4121, 266, 29, "Vandelay Industries Ltd", "e"],
["Jesus McCormick", 7154, 248, 28, "Nordyne Defense Dynamics PC", "c"],
["Calvin Richardson", 9632, 800, 90, "QWERTY Logistics Inc.", "e"],
["Jeff Scott", 6163, 219, 3, "Fake Brothers Corp.", "c"],
["Joshua Perkins", 2832, 826, 76, "Mooby Corp Corporation", "a"],
["Virginia Steele", 4543, 659, 75, "Mainway Toys Associates", "d"],
["Winifred Moss", 505, 167, 77, "General Products Foundation", "e"],
["Ruth Flowers", 1922, 280, 90, "Praxis Corporation Inc.", "a"],
["Betty Stevenson", 3916, 351, 78, "Big Kahuna Burger Incorporated", "b"],
["Alvin Buchanan", 8259, 852, 62, "Three Waters Limited", "d"],
["Jeremiah Wolfe", 3517, 888, 53, "Powell Motors PC", "e"],
["Ethan Newman", 239, 666, 83, "Wentworth Industries Ltd", "c"],
["Norman Mason", 4052, 617, 19, "Smith and Co. Inc.", "e"],
["Jerome Schultz", 7375, 661, 30, "Primatech Consulting", "a"],
["Ernest Kelley", 3868, 453, 97, "Smith and Co. Incorporated", "b"],
["Lucinda Carlson", 2665, 286, 6, "Monks Diner Consulting", "d"],
["Ronald Dunn", 8059, 646, 75, "LexCorp Corporation", "c"],
["Douglas Edwards", 5054, 908, 4, "Industrial Automation Inc.", "b"],
["Maude Palmer", 9043, 603, 74, "LexCorp Industries", "c"],
["Marion Sims", 3187, 124, 46, "Thrift Bank Ltd", "e"],
["Lewis Burns", 4562, 856, 79, "Chez Quis Associates", "d"],
["Myrtie McBride", 4827, 533, 17, "LexCorp Foundation", "a"],
["Marguerite Ramirez", 1580, 532, 98, "Extensive Enterprise LLP", "c"],
["Vernon Lloyd", 921, 485, 4, "Western Gas & Electric Corporation", "c"],
["Isabelle Carroll", 3266, 640, 66, "Virtucon Limited", "b"],
["Lilly Daniel", 1762, 585, 11, "U.S. Robotics and Mechanical Men PC", "d"],
["Cecilia Romero", 6749, 440, 67, "Moes Tavern Industries", "d"],
["Florence Mann", 330, 780, 65, "Big Kahuna Burger PC", "d"],
["Lelia Willis", 9282, 799, 87, "Three Waters LLC", "d"],
["Adelaide McCoy", 1872, 544, 2, "QWERTY Logistics Corp.", "a"],
["Laura Gonzales", 7293, 43, 62, "Roboto Industries Foundation", "d"],
["Jeremiah Pittman", 7522, 583, 58, "Globo-Chem PC", "c"],
["Franklin Robbins", 4541, 167, 8, "Cogswell Cogs Corp.", "d"],
["Carl Lowe", 2834, 943, 41, "Globo-Chem Incorporated", "d"],
["Alexander Gordon", 3139, 608, 7, "Initech Ltd", "d"],
["Lillian Barber", 3130, 529, 19, "Demo Company Institute", "a"],
["Ray Hanson", 3485, 824, 34, "Input Inc. Corporation", "d"],
["Norman Wallace", 5577, 322, 32, "SpringShield Corp.", "d"],
["Ricky Fields", 5809, 989, 14, "LexCorp Foundation", "d"],
["Todd Houston", 4668, 98, 28, "Krustyco Limited", "c"],
["Julia Carter", 4298, 646, 42, "Monarch Playing Card Co. Ltd", "a"],
["Carlos Ferguson", 531, 63, 71, "Gadgetron Consulting", "b"],
["Ricardo Diaz", 8126, 494, 86, "Omni Consimer Products Consulting", "a"],
["Lula Harmon", 1159, 817, 64, "General Products Inc.", "b"],
["Theresa Morton", 9885, 293, 92, "Niagular Inc.", "b"],
["Martin Salazar", 204, 61, 56, "Mammoth Pictures Consulting", "c"],
["Henrietta Barton", 6819, 97, 34, "Axis Chemical Co. Limited", "c"],
["Austin Burgess", 811, 33, 57, "Compuglobalhypermeganet Limited", "e"],
["Harold Barnett", 948, 922, 38, "Videlectrix Consulting", "b"],
["Roger Hernandez", 2373, 853, 4, "Globo-Chem Limited", "e"],
["Katie Castro", 951, 47, 77, "The Krusty Krab LLC", "d"],
["Lucille Cole", 2382, 352, 74, "Kumatsu Motors LLC", "e"],
["Jean Fitzgerald", 5891, 811, 13, "Wayne Enterprises Company", "b"],
["Lois Bryant", 5043, 757, 15, "Taggart Transcontinental Corporation", "e"],
["Nell Barrett", 9817, 751, 66, "Osato Chemicals Limited", "c"],
["Matthew McDonald", 4582, 965, 55, "Carrys Candles Foundation", "d"],
["Melvin Carter", 6057, 838, 32, "C.H. Lavatory and Sons Corporation", "c"],
["Landon Robinson", 3276, 263, 19, "Powell Motors PC", "e"],
["Dominic Price", 3992, 612, 13, "Duff Brewing Company Incorporated", "b"],
["Hannah Barber", 6143, 977, 87, "Klimpys Associates", "a"],
["Jeremiah Warner", 4724, 931, 27, "Leeding Engines Ltd. Industries", "b"],
["Leila Yates", 1130, 796, 56, "123 Warehousing Ltd", "e"],
["Madge Greer", 8250, 475, 9, "Strickland Propane Associates", "e"],
["Elva Boyd", 4560, 470, 77, "Krustyco Corp.", "e"],
["Maurice Frank", 2834, 748, 100, "General Services Corporation Associates", "b"],
["Jennie Pierce", 8532, 122, 83, "Gringotts LLP", "a"],
["Isaiah Fitzgerald", 5156, 727, 88, "Leeding Engines Ltd. LLP", "e"],
["Dominic Walton", 2578, 408, 85, "Globo Gym American Corp PC", "e"],
["Stella Phillips", 4101, 102, 66, "Gringotts Industries", "e"],
["Sam Casey", 52, 755, 28, "C.H. Lavatory and Sons PC", "e"],
["Viola Owens", 7018, 29, 1, "Dunder Mifflin Foundation", "d"],
["Mark Reese", 5232, 104, 8, "Virtucon Consulting", "c"],
["Aaron Hernandez", 8230, 607, 47, "Spade and Archer Incorporated", "e"],
["Lizzie Atkins", 6915, 906, 43, "McMahon and Tate Corp.", "b"],
["Theodore Ortiz", 2639, 755, 78, "ZiffCorp LLC", "a"],
["Rosa Warren", 629, 76, 9, "Bluth Company Institute", "e"],
["Lilly Baker", 3847, 783, 93, "Compuglobalhypermeganet Ltd", "e"],
["Jordan Barton", 355, 517, 73, "Osato Chemicals Consulting", "c"],
["Alejandro Miles", 8664, 999, 95, "LexCorp Incorporated", "c"],
["Carl Rowe", 5811, 861, 4, "Mr. Sparkle Ltd", "a"],
["Harold Harvey", 3936, 889, 1, "Milliways Corp.", "c"],
["Henrietta Rodgers", 6137, 871, 56, "Flowers By Irene Consulting", "e"],
["Lura Walton", 3777, 801, 42, "Roboto Industries Incorporated", "a"],
["Sadie Richards", 7989, 862, 31, "Extensive Enterprise Ltd", "d"],
["Isabel Gray", 735, 88, 83, "Roxxon Corporation", "e"],
["Isabelle King", 2587, 52, 34, "Keedsler Motors Limited", "a"],
["Dennis Cobb", 283, 124, 86, "Foo Bars Institute", "d"],
["Philip Carroll", 5692, 590, 36, "Gadgetron Limited", "a"],
["Mario Robinson", 7520, 274, 77, "Sombra Corporation Foundation", "a"],
["Harriet Soto", 3071, 989, 52, "ABC Telecom Foundation", "e"],
["Rose Swanson", 9131, 762, 86, "Cogswell Cogs Consulting", "a"],
["Andrew Gilbert", 8563, 2, 68, "Thrift Bank Company", "c"],
["Ola Barnett", 2824, 39, 25, "Atlantic Northern Industries", "a"],
["Jeff Watson", 8337, 805, 13, "Corellian Engineering Corporation Associates", "a"],
["Phillip Reeves", 9334, 26, 93, "Duff Brewing Company Incorporated", "c"],
["Jerome Washington", 3944, 60, 78, "Cogswell Cogs Inc.", "a"],
["Luke Hicks", 3916, 23, 97, "Mooby Corp Institute", "a"],
["Adeline Reid", 2441, 557, 97, "Ajax Company", "c"],
["Derrick French", 7306, 227, 99, "Mainway Toys Associates", "e"],
["Jeffrey Rice", 3530, 45, 78, "Zevo Toys Company", "b"],
["Luella Butler", 25, 872, 9, "123 Warehousing Corporation", "e"],
["Wesley Price", 6654, 516, 3, "Universal Export Corp.", "e"],
["Gene Brown", 7172, 99, 78, "Acme Corp Corporation", "a"],
["Catherine McGee", 2433, 634, 57, "Gringotts Corp.", "e"],
["Vincent Miles", 346, 523, 76, "Mooby Corp Industries", "e"],
["Tyler Harris", 4302, 361, 7, "Monarch Playing Card Co. Foundation", "e"],
["Jorge Jacobs", 1839, 298, 59, "Corellian Engineering Corporation Corporation", "e"],
["Roy Sparks", 4471, 162, 3, "Chez Quis Consulting", "b"],
["Pauline Vasquez", 943, 705, 59, "Mammoth Pictures PC", "d"],
["Ida Baker", 943, 275, 2, "Chotchkies Ltd", "c"],
["Isabelle Gomez", 1999, 914, 29, "Three Waters Ltd", "b"],
["Wayne Wilson", 4974, 562, 85, "Sample inc PC", "a"],
["Juan Warner", 4672, 169, 31, "Galaxy Corp Associates", "e"],
["Alta Walker", 6045, 373, 17, "Acme inc. LLC", "e"],
["Eugenia Graves", 1539, 26, 96, "St. Anky Beer Inc.", "d"],
["Aaron Knight", 1735, 569, 12, "Big T Burgers and Fries Associates", "e"],
["Sophia Myers", 3845, 760, 33, "ZiffCorp LLC", "a"],
["Cecilia Robinson", 9521, 628, 75, "Galaxy Corp Company", "e"],
["Kenneth Wallace", 4947, 626, 97, "Allied Biscuit Corp.", "b"],
["Loretta Brooks", 8344, 416, 50, "Water and Power Consulting", "c"],
["David Doyle", 2951, 150, 15, "Acme Corp Institute", "c"],
["Kathryn Lynch", 3530, 142, 60, "Gizmonic Institute Institute", "a"],
["Martin Moran", 2906, 489, 96, "Wernham Hogg Company", "d"],
["Carrie Garner", 254, 795, 84, "Vandelay Industries Corporation", "b"],
["Lydia Johnson", 1077, 267, 3, "Flowers By Irene Foundation", "d"],
["James Higgins", 6527, 515, 48, "Demo Company Consulting", "d"],
["Christopher Duncan", 2850, 847, 78, "Tip Top Cafe Corp.", "d"],
["Emma Bush", 5149, 96, 18, "Plow King Inc.", "a"],
["Ricky Craig", 5590, 690, 32, "United Fried Chicken Corporation", "b"],
["Teresa Casey", 2547, 989, 17, "North Central Positronics LLP", "b"],
["Eula Morrison", 8361, 558, 50, "Atlantic Northern Limited", "c"],
["Birdie Mills", 3152, 560, 24, "Charles Townsend Agency LLP", "c"],
["Sylvia Cole", 2676, 415, 84, "Sample Company Ltd", "c"],
["Ethan Chapman", 9492, 765, 83, "Leeding Engines Ltd. Institute", "b"],
["Ronald Lawrence", 1399, 166, 98, "Chasers Corporation", "e"],
["Ray Castillo", 7394, 615, 13, "Demo Company Industries", "a"],
["Brandon Mathis", 3386, 833, 55, "LexCorp PC", "e"],
["Bessie Foster", 1668, 584, 22, "Minuteman Cafe Foundation", "a"],
["Rosetta Walton", 6664, 193, 65, "Primatech Consulting", "b"],
["Myrtie Castillo", 4726, 782, 86, "Input Inc. Limited", "a"],
["Ethan Mendez", 6412, 271, 29, "Gizmonic Institute Limited", "b"],
["Cory Carroll", 9434, 314, 71, "Burleigh and Stronginthearm Foundation", "c"],
["Gordon Caldwell", 71, 639, 10, "Big T Burgers and Fries Associates", "e"],
["Ricky Aguilar", 5868, 185, 56, "Initrode Corp.", "d"],
["Dominic Pope", 6990, 945, 32, "Ajax Incorporated", "a"],
["Belle McGuire", 2758, 541, 3, "Megadodo Publications Incorporated", "d"],
["Clarence Lawson", 4226, 776, 92, "Gizmonic Institute Inc.", "e"],
["Stephen Reeves", 8237, 822, 72, "ZiffCorp Company", "c"],
["Maude Casey", 225, 670, 9, "Praxis Corporation Incorporated", "e"],
["Mollie Hunt", 9300, 897, 75, "Minuteman Cafe Ltd", "e"],
["Corey Gray", 3632, 205, 97, "Chez Quis LLP", "a"],
["Rhoda Norris", 4986, 371, 4, "Flowers By Irene Ltd", "e"],
["Olivia Holland", 8421, 596, 81, "Tip Top Cafe Institute", "a"],
["Edwin Allison", 3865, 905, 77, "Mammoth Pictures Inc.", "a"],
["Ricardo Walton", 1487, 736, 93, "Wentworth Industries Consulting", "b"],
["Tyler Carter", 1030, 430, 35, "Sixty Second Avenue Inc.", "b"],
["Ruth Park", 5611, 883, 74, "Globo Gym American Corp Industries", "a"],
["Chase Snyder", 3502, 398, 77, "Megadodo Publications Corp.", "d"],
["Jose Hunter", 5684, 968, 59, "Dunder Mifflin Foundation", "e"],
["Henry Reynolds", 8854, 942, 35, "The Frying Dutchman Industries", "a"],
["Sara Keller", 7213, 515, 0, "Megadodo Publications LLP", "a"],
["Vera Wong", 1900, 952, 29, "Taco Grande Associates", "b"],
["Claudia Horton", 8056, 173, 2, "Ajax Associates", "b"],
["Julia Hall", 1491, 346, 19, "Big Kahuna Burger PC", "d"],
["Kathryn Shelton", 7856, 41, 54, "LuthorCorp PC", "a"],
["Raymond Lawson", 7326, 588, 58, "Big T Burgers and Fries LLP", "e"],
["Lucille Powell", 5861, 132, 29, "Transworld Consortium LLC", "c"],
["Alejandro Spencer", 1470, 923, 86, "Wentworth Industries Corp.", "e"],
["Cody Holland", 3962, 576, 72, "Milliways Inc.", "d"],
["Gary McGuire", 2879, 674, 11, "Osato Chemicals Incorporated", "e"],
["Eula Malone", 5915, 213, 2, "The New Firm LLC", "e"],
["Nell Alvarado", 723, 569, 26, "Wentworth Industries Corp.", "e"],
["Hattie Bailey", 4670, 297, 75, "Osato Chemicals Foundation", "a"],
["Manuel Brady", 6459, 819, 65, "Stay Puft Corporation Incorporated", "c"],
["Jane George", 9429, 879, 62, "Sonky Rubber Goods Limited", "b"],
["Adeline Hicks", 5775, 110, 24, "Tessier-Ashpool Inc.", "a"],
["Ian Lyons", 9005, 331, 91, "Acme inc. LLP", "c"],
["Jeremy Abbott", 1701, 213, 48, "Cogswell Cogs Foundation", "e"],
["Bertie Wright", 2205, 802, 73, "Petrox Oil Company Corp.", "a"],
["Genevieve Rodriquez", 5796, 863, 42, "ZiffCorp Industries", "d"],
["Ida Thornton", 6879, 927, 33, "Virtucon Foundation", "b"],
["Flora Collins", 3801, 15, 86, "Rouster and Sidewazys Company", "b"],
["Danny Pearson", 4290, 769, 15, "Keedsler Motors Incorporated", "c"],
["Anne Turner", 7239, 804, 18, "Western Gas & Electric Corporation", "e"],
["Sam McCarthy", 3503, 577, 75, "Galaxy Corp PC", "a"],
["Michael Olson", 2190, 676, 21, "Spade and Archer Ltd", "a"],
["Carl Sherman", 5005, 945, 95, "Sonky Rubber Goods Consulting", "d"],
["Jennie Adams", 1168, 252, 91, "Wentworth Industries Corporation", "e"],
["Jeffery Anderson", 1575, 78, 35, "Plow King Company", "b"],
["Landon Norton", 3364, 775, 14, "Universal Export LLC", "c"],
["Lettie West", 280, 87, 2, "Initrode Corp.", "c"],
["Carlos Bridges", 1292, 244, 59, "Nordyne Defense Dynamics Foundation", "c"],
["Curtis Leonard", 987, 324, 63, "Axis Chemical Co. Corp.", "c"],
["Ryan Lynch", 4533, 114, 86, "Monarch Playing Card Co. Institute", "a"],
["Philip Luna", 4207, 921, 98, "123 Warehousing Inc.", "b"],
["Sam Townsend", 4979, 760, 58, "Virtucon Inc.", "b"],
["Eula Brewer", 1929, 417, 85, "Acme Corp Consulting", "a"],
["Katharine Phillips", 6919, 227, 10, "Transworld Consortium Institute", "e"],
["Barry Curry", 5168, 780, 19, "Spade and Archer PC", "c"],
["Adelaide Weber", 6532, 485, 23, "Thrift Bank Incorporated", "b"],
["Stephen Reid", 1115, 923, 11, "Sonky Rubber Goods LLP", "b"],
["Seth McDonald", 9778, 374, 91, "Monarch Playing Card Co. Ltd", "e"],
["Evelyn Higgins", 3346, 824, 75, "Chotchkies Corp.", "b"],
["Jane Underwood", 1186, 321, 96, "Three Waters Industries", "b"],
["Isaac Cruz", 5024, 155, 39, "The New Firm Corporation", "e"],
["Rosie Sutton", 1074, 219, 59, "Leeding Engines Ltd. Incorporated", "d"],
["Carlos Perez", 5122, 808, 9, "Taggart Transcontinental Inc.", "a"],
["Lucy Hodges", 2920, 601, 27, "Initech Associates", "d"],
["Ruby Parker", 1723, 741, 96, "Big T Burgers and Fries Consulting", "d"],
["Maurice Martin", 1748, 836, 66, "Rouster and Sidewazys LLC", "c"],
["Marian Johnson", 1480, 470, 28, "Sixty Second Avenue Limited", "a"],
["Leon Carroll", 4528, 490, 47, "Burleigh and Stronginthearm LLC", "a"],
["Joel Lynch", 6989, 699, 47, "Primatech Company", "a"],
["Lucas Byrd", 9990, 919, 9, "Blammo Consulting", "e"],
["Gregory Adams", 8558, 135, 92, "Mammoth Pictures Foundation", "c"],
["Elijah Santos", 3724, 446, 88, "Monks Diner LLC", "c"],
["Cordelia Haynes", 340, 780, 83, "Atlantic Northern Foundation", "b"],
["Jason Barnes", 6271, 445, 77, "Sto Plains Holdings Consulting", "b"],
["Lura Torres", 101, 231, 58, "St. Anky Beer Industries", "a"]
];
function filterA(line) {
return line[1] <= 5000;
}
function filterB(line) {
return line[2] <= 500;
}
function filterC(line) {
return line[3] <= 50;
}
Ready to run.
Test | Ops/sec | |
---|---|---|
Lodash.js |
| ready |
Lazy.js |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.