UNPKG

enumerable-js

Version:

Utility functions for all types of collections

398 lines (340 loc) 10.7 kB
(function(){ 'use strict'; var root = this; // this is used to "break" out of an each loop var breaker = {}; // store the old enumerable var oldEnumerable = root.enumerable; var enumerable = {}; enumerable.VERSION = '0.0.1'; // if enumerable was being used, it may be reinstated by invoking noConflict. However, this will remove the enumerable // library unless it is saved to a variable. enumerable.noConflict = function noConflict(){ root.enumerable = oldEnumerable; return enumerable; }; // returns the result of combining all items in the collection into one, via the seed object enumerable.reduce = enumerable.inject = enumerable.foldl = function reduce(callback, seed, context){ context = context || this; this.each(function(item){ seed = callback.call(context, seed, item); }); return seed; }; // returns an array of all items for which the callback returns truthy enumerable.filter = enumerable.select = enumerable.findAll = function filter(callback, context){ context = context || this; var result = []; this.each(function(item){ if(callback.call(context, item)){ result.push(item); } }); return result; }; // returns the first item for which the callback returns truthy enumerable.find = function find(callback, context){ context = context || this; var result = null; try { this.each(function(item){ if(callback.call(context, item)){ result = item; throw breaker; } }); } catch(e) { if(e !== breaker){ throw e; } } return result; }; // if no condition is supplied, returns the number of items total // if a function is supplied as the condition, counts the number of items for which the condition passes // otherwise, counts the number of items that strictly equal the passed in object enumerable.count = function count(condition, context){ context = context || this; var result = 0; var callback = typeof condition === 'function' ? condition : function(item){ return item === condition; }; this.each(function(item){ condition ? callback.call(context, item) && result++ : result++; }); return result; }; // returns true if the callback on every item is truthy enumerable.all = function all(callback, context){ context = context || this; try { this.each(function(item){ if(!callback.call(context, item)){ throw breaker; } }); } catch(e) { if(e === breaker){ return false; } else { throw e; } } return true; }; // returns true if the callback on any item is truthy enumerable.any = function any(callback, context){ context = context || this; return !this.all(function(item){ return !callback.call(context, item); }); }; // IMPORTANT TO NOTE: JavaScript primitives are immutable, and this function is designed // to mutate each object, so it will not work if the `each` item is a primitive. enumerable.mapInPlace = function mapInPlace(callback, context){ context = context || this; this.each(function(item){ item = callback.call(context, item); }); return this; }; // returns an array of ths result invoking the callback on each item in the collection enumerable.mapToArray = function mapToArray(callback, context){ context = context || this; var result = []; this.each(function(item){ result.push(callback.call(context, item)); }); return result; }; // if two objects are passed in, it will extend properties of the first object to the second object. // otherwise, if just one is passed in, it will extend enumerable to that object. enumerable.extend = function extend(obj1, obj2){ if(!obj2){ this.extend(this, obj1); } else { for(var key in obj1){ obj2[key] = obj1[key]; } } }; // returns all the items in the collection in an array format enumerable.toArray = function toArray(){ return this.mapToArray(function(item){ return item; }); }; // executes the callback on every item in the collection num amount of times enumerable.cycle = function cycle(callback, num, context){ context = context || this; if (typeof num !== 'number') { throw new SyntaxError('need a number passed in as the second argument'); } num = Math.floor(Math.abs(num)); while(num){ this.each.call(context, callback); num--; } return this; }; // returns the first item for which the callback returns falsy enumerable.detect = function detect(callback, context){ context = context || this; return this.find(function(node){ return !callback.call(context, node); }); }; // invokes the callback on num-consecutive items in an array format // [1,2,3,4].eachCons(function(console.log(nums)), 3); // #=> [1,2,3] // #=> [2,3,4] enumerable.eachCons = function eachCons(callback, num, context){ context = context || this; var index = 0; var self = this; try { this.each(function(){ var collection = []; self.eachUntilN(function(node){ collection.push(node); }, num, this, index); if (collection.length !== num) { throw breaker; } callback.call(context, collection); index++; }); } catch(e){ if (e !== breaker) { throw e; } } return this; }; // slices the collection up into num-sized arrays, and invokes the callback on them enumerable.eachSlice = function eachSlice(callback, num, context){ if (typeof num !== 'number') { throw new SyntaxError('need a number passed in as a second argument') } context = context || this; var currentSlice = []; this.each(function(item){ currentSlice.push(item); if (currentSlice.length == num) { callback.call(context, currentSlice); currentSlice = []; } }); if (currentSlice.length) { callback.call(context, currentSlice); } return this; } // iterates the given callback for the first N elements only. If a start index is given, it starts at that element enumerable.eachUntilN = function eachUntilN(callback, num, context, start){ start = start || 0; context = context || this; var index = -1; if (typeof num !== 'number' || num < 0) { throw new TypeError('need a positive integer number as a second argument') } try { this.each(function(item){ index++; if (index < start) { return; } callback.call(context, item); num--; if (num === 0) { throw breaker; } }); } catch(e) { if (e !== breaker) { throw e; } } return this; }; // returns the first item in the collection. If a numerical argument is given, returns the first num arguments in an array enumerable.first = function first(num){ var count = 0; var result; try { if (typeof num !== 'number') { return this.each(function(item){ result = item; throw breaker; }); } else { result = []; this.each(function(item){ result.push(item); if (result.length == num){ throw breaker; } }) } } catch(e) { if (e !== breaker) { throw e; } } return result; }; // returns the maximal item in a collection as determined by the callback enumerable.max = function max(callback, context){ if (typeof callback !== 'function') { throw new SyntaxError('need a callback argument') } context = context || this; var maxObj, maxValue, currentValue; this.each(function(currentObj){ currentValue = callback.call(context, currentObj); if (typeof maxObj === 'undefined' || currentValue > maxValue) { maxObj = currentObj; maxValue = currentValue; } }); return maxObj; }; // returns the maximal item in a collection as determined by the callback enumerable.min = function min(callback, context){ if(typeof callback !== 'function'){ throw new SyntaxError('need a callback argument') } context = context || this; var minObj, minValue, currentValue; this.each(function(currentObj){ currentValue = callback.call(context, currentObj); if (typeof minObj === 'undefined' || currentValue < minValue) { minObj = currentObj; minValue = currentValue; } }); return minObj; }; // returns true if none of the items return truthy from the callback enumerable.none = function none(callback, context){ context = context || this; return this.all(function(item){ return !callback.call(context, item); }); }; // returns a boolean of whether a collection has num-number of items that return truthy from the callback enumerable.hasN = function hasN(callback, num, context){ context = context || this; num = num || 1; try { this.each(function(item){ if(callback.call(context, item)){ num--; } if(num < 0){ throw breaker; } }) } catch (e){ if (e !== breaker) { throw e; } } return num == 0; }; // returns two arrays, one with items which pass the callback, and one which does not enumerable.partition = function partition(callback, context){ var pass = []; var fail = []; this.each(function(item){ if(callback.call(context, item)){ pass.push(item); } else { fail.push(item); } }) return [pass, fail]; }; // invokes the callback in reverse order enumerable.reverseEach = function reverseEach(callback, context){ var items = this.toArray(); for(var i = items.length - 1; i >= 0; i--){ callback.call(context, items[i]); } return this; }; // returns an array of items sorted in ascending order by the callback enumerable.sort = function(callback, context){ return this.toArray().sort(function(node1, node2){ var result1 = callback.call(context, node1); var result2 = callback.call(context, node2); if (result1 > result2) { return 1; } else if (result1 < result2) { return -1; } else { return 0; } }); }; if (typeof exports !== 'undefined') { if (typeof module !== 'undefined' && module.exports) { exports = module.exports = enumerable; } exports.enumerable = enumerable; } else { root.enumerable = enumerable } }).call(this);