UNPKG

gauss

Version:

JavaScript statistics, analytics, and set library

411 lines (402 loc) 12.7 kB
/** * Collection: n-dimensional array/set * Extends upon the Array datatype for set operations. * @author Fredrick Galoso */ (function() { var Collection = function(values) { "use strict"; var collection = Array.isArray(values) ? values : Array.prototype.slice.call(arguments); if (Array.prototype.slice.call(arguments).length === 0) { collection = []; } var result = function(value, callback) { if (callback) { return callback(value); } else { return value; } }; Object.defineProperty(collection, 'extend', { /** * Return a Collection extended with named functions. * @param methods Object { 'functionName': function() {} } */ value: function(methods, callback) { for (var method in methods) { Object.defineProperty(this, method, { value: methods[method], writable: true, enumerable: false }); } return result(this, callback); }, writable: true, enumerable: false }); collection.extend({ indexOf: function(element, callback) { var index = -1; for (var i = 0; i < this.length; i++) { if (this[i] === element) { index = i; break; } } return result(index, callback); }, /** * Returns the first index of an element that matches a condition. * @param predicate {Function} * @param callback {Function} */ indexBy: function(predicate, callback) { var index = -1; for (var i = 0; i < this.length; i++) { if (predicate(this[i])) { index = i; break; } } return result(index, callback); }, /** * Returns the indices of all elements that match a value. * @param element {Object} * @param callback {Function} */ indicesOf: function(element, callback) { var start = this.indexOf(element), end = this.lastIndexOf(element), indices = new Collection(); if (start === end) { indices.push(start); } else { for (var i = start; i <= end; i++) { if (this[i] === element) { indices.push(i); } } } return result(indices, callback); }, /** * Returns all indices of an element that match a condition. * @param predicate {Function} * @param callback {Function} */ indicesBy: function(predicate, callback) { var start = 0, end = this.length - 1, indices = new Collection(); if (start === end) { indices.push(start); } else { for (var i = start; i <= end; i++) { if (predicate(this[i])) { indices.push(i); } } } return result(indices, callback); }, /** * Returns the last index of an element that matches a condition. * @param predicate {Function} * @param callback {Function} */ lastIndexBy: function(predicate, callback) { var index = -1; for (var i = this.length - 1; i >= 0; i--) { if (predicate(this[i])) { index = i; break; } } return result(index, callback); }, /** * Returns all the elements that match a condition. * @param predicate {Function or Object} * @param callback {Function} */ find: function(predicate, callback) { var results = new Collection(), comparator = function(e) { return e === true; }; if (typeof predicate === 'object') { for (var i = 0; i < this.length; i++) { var conditions = []; for (var key in predicate) { conditions.push(predicate[key] === this[i][key]); } if (conditions.every(comparator)) { results.push(this[i]); } } } else { for (var i = 0; i < this.length; i++) { if (predicate(this[i])) { results.push(this[i]); } } } return result(results, callback); }, /** * Returns the first element that matches a condition. * @param predicate {Function or Object} * @param callback {Function} */ findOne: function(predicate, callback) { var element, comparator = function(e) { return e === true; }; if (typeof predicate === 'object') { for (var i = 0; i < this.length; i++) { var conditions = []; for (var key in predicate) { conditions.push(predicate[key] === this[i][key]); } if (conditions.every(comparator)) { element = this[i]; break; } } } else { for (var i = 0; i < this.length; i++) { if (predicate(this[i])) { element = this[i]; break; } } } return result(element, callback); }, mode: function(callback) { var map = {}, count = 1, modes = new Collection(); for (var i = 0; i < this.length; i++) { var e = this[i]; if (map[e] == null) { map[e] = 1; } else { map[e]++; } if (map[e] > count) { modes = new Collection(e); count = map[e]; } else if (map[e] == count) { modes.push(e); count = map[e]; } } if (modes.length === 1) { modes = modes[0]; } return result(modes, callback); }, /** * Returns the number of occurrences of value within a data set. * @param element {Number or String} * @param callback {Function} */ frequency: function(element, callback) { var freq = 0; if (this.indexOf(element) !== -1) { var buffer = this.copy().sort(); freq = buffer.lastIndexOf(element) - buffer.indexOf(element) + 1; } return result(freq, callback); }, /** * Returns an Object containing the (frequency) distribution of values within the Collection. * @param format {String} Override distribution format with Percent Distribution. Default: Raw count */ distribution: function(format, callback) { var array = this.copy().sort(), distribution = {}; for (var i = 0; i < array.length;) { distribution[array[i]] = (format === 'relative') ? array.frequency(array[i]) / array.length : array.frequency(array[i]); i = (array.lastIndexOf(array[i]) + 1); } return result(distribution, callback); }, /** * Return Collection appended with Array (In place Array.concat). * @param that Array * @param callback Function */ append: function(that, callback) { collection = this.push.apply(this, that); return result(this, callback); }, /** * Predicates */ /* Test for equality between two collections */ equal: function(that, callback) { var equality = !(this < that || that < this); return result(equality, callback); }, /* Returns a clone of the Collection object */ clone: function(callback) { var object = Array.isArray(this) ? [] : {}; for (var i in this) { if (i === 'clone') { continue; } if (this[i] && typeof this[i] === 'object') { object[i] = this[i].clone(); } else { object[i] = this[i]; } } return result(object, callback); }, /* Returns a copy of the values in a Collection object */ copy: function(callback) { var copy = new Collection(this.slice()); return result(copy, callback); }, /* Returns a vanilla Array of values */ toArray: function(callback) { var array = Array.prototype.slice.call(this); return result(array, callback); }, /** * [Accessor methods] */ concat: function() { var args = Array.prototype.slice.call(arguments); var end = args[args.length - 1]; if (typeof end === 'function') { if (typeof args[0] !== 'number') { return end(new Collection(this.toArray().concat(args[0]))); } else { return end(new Collection(this.toArray().concat(args.slice(0, args.length - 1)))); } } else if (typeof args[0] !== 'number') { return new Collection(this.toArray().concat(args[0])); } else { return new Collection(this.toArray().concat(args)); } }, slice: function(begin, end, callback) { var args = Array.prototype.slice.call(arguments); if (args.length === 3) { return callback(new Collection(this.toArray().slice(begin, end))); } else if (args.length === 2) { if (typeof args[1] === 'function') { return callback(new Collection(this.toArray().slice(begin))); } else { return new Collection(this.toArray().slice(begin, end)); } } else if (args.length === 1) { return new Collection(this.toArray().slice(begin)); } else { return new Collection(this.toArray().slice()); } }, split: function(predicate, callback) { var split = new Collection(); split.push(this.toArray() .filter(function(e) { return !predicate(e); })); split.push(this.toArray() .filter(predicate)); return result(split, callback); }, unique: function(callback) { var array = this.copy().sort(), unique = new Collection(); for (var i = 0; i < array.length;) { unique.push(array[i]); i = (array.lastIndexOf(array[i]) + 1); } return result(unique, callback); }, /** * [Iteration methods] */ filter: function(callback, next) { var filter = new Collection(this.toArray().filter(callback)); return result(filter, next); }, every: function(callback, next) { var every = this.toArray().every(callback); return result(every, next); }, map: function(callback, next) { var map = new Collection(this.toArray().map(callback)); return result(map, next); }, some: function(callback, next) { var some = this.toArray().some(callback); return result(some, next); }, reduce: function(callback, initialValue, next) { var args = Array.prototype.slice.call(arguments); if (args.length === 3) { return next(this.toArray().reduce(callback, initialValue)); } else if (args.length === 2) { if (typeof args[1] === 'function') { return next(this.toArray().reduce(callback)); } else { return this.toArray().reduce(callback, initialValue); } } else { return this.toArray().reduce(callback); } }, reduceRight: function(callback, initialValue, next) { var args = Array.prototype.slice.call(arguments); if (args.length === 3) { return next(this.toArray().reduceRight(callback, initialValue)); } else if (args.length === 2) { if (typeof args[1] === 'function') { return next(this.toArray().reduceRight(callback)); } else { return this.toArray().reduceRight(callback, initialValue); } } else { return this.toArray().reduceRight(callback); } }, union: function(that, callback) { var union = this.append(that).unique(); return result(union, callback); } }); return collection; }; Array.prototype.toCollection = function() { return new Collection(this); }; if (typeof window !== 'undefined') { window.gauss = (typeof window.gauss === 'undefined' ? {} : window.gauss); window.gauss.Collection = Collection; } else { exports = module.exports = Collection; } })();