_.mixin({ /** * Fill a destination array with values from source. * * Useful when you need to mutate an existing array, i.e. without using * `.concat`. * * @param {Array} destination The array to copy values to. * @param {Array} source The array to copy values from. * @example * * _.fill([1], [2, 3]); * // => [1, 2, 3] */ fill: function(destination, source) { _.each(source, function(value) { destination.push(value); }); return destination; }, /** * Batches an array into `n` sized chunks. * * @param {Array} items The values to batch. * @param {number} batchSize The size of chunks to return. * @example * * _.batch([1, 2, 3, 4, 5, 6, 7, 8], 3); * // => [[1, 2, 3], [4, 5, 6], [7, 8]] */ batch: function(items, batchSize) { if (batchSize <= 0) { throw new Exception('batch must be > 0 in _.batch'); } var batched = []; while (items.length) { batched.push(items.splice(0, Math.ceil(batchSize))); } return batched; }, /** * Logs its arguments and returns it's first argument. */ log: function (returnValue) { var console = window.console; console.log.apply(console, arguments); return returnValue; }, /** * Returns a function that negates the function passed. * * @example * * var even = function(n) { return n % 2 == 0; }; * var odd = _.not(even); * odd(2) // => false; * _.filter([{a: 1}, {a:1, b:2}, {b:2}], _.not('a')); // => {b:2} */ not: function() { var cb = _.createCallback.apply(_, arguments); return function() { return !cb.apply(null, arguments); }; }, /** * Returns a number clamped between a minimum and maximum value. * If the maximum isn't provided, only clamps from the bottom. * * @param {number} value The value to clamp. * @param {number} minimum The minimum value. * @param {number?} maximum The maximum value. * @returns {number} A value between minimum and maximum. * * @example * * _.clamp(4, 2, 6); // => 4 * _.clamp(2, 3, 5); // => 3 * _.clamp(7, 2); // => 7 * _.clamp(7, 2, 5); // => 5 */ clamp: function(value, minimum, maximum) { if (maximum == null) { maximum = Number.MAX_VALUE; } if (maximum < minimum) { var swap = maximum; maximum = minimum; minimum = swap; } return Math.max(minimum, Math.min(value, maximum)); } }); describe('_.fill', function() { it('fills the destination array', function () { var destination = [1]; var source = [2, 3]; var returnValue = _.fill(destination, source); expect(source).toEqual([2, 3]); expect(destination).toEqual([1, 2, 3]); expect(returnValue).toBe(destination); }); it('works the same as concat', function() { expect(function(destination, source) { var expected = destination.concat(source); _.fill(destination, source); return _.isEqual(destination, expected); }).forAll(qc.array, qc.array); }); }); describe('_.batch', function () { it('chunks are never greater than the limit', function () { expect(function(list, limit) { if (limit > 0) { var batched = _.batch(list, limit); return _.all(batched, function(batch) { return batch.length <= limit; }); } }).forAll(qc.array, qc.ureal.large); }); }); describe('_.log', function () { beforeEach(function () { spyOn(console, 'log'); }); it('logs all it\'s arguments', function () { _.log(1, 2, 3); expect(console.log).toHaveBeenCalledWith(1, 2, 3); }); it('returns it\'s first argument', function() { var returnValue = _.log(1, 2, 3); expect(returnValue).toBe(1); }); it('is chainable', function () { _([1, 2, 3]).map(function(n) { return n * 2; }).log(); expect(console.log).toHaveBeenCalledWith([2, 4, 6]); }); }); describe('_.not', function () { it('negates a function', function () { var even = function(n) { return n % 2 === 0; }; var odd = _.not(even); expect(odd(3)).toBe(true); }); it('can find falsey attributes', function () { var obj = {a: 3, b: false}; expect(_.not('a')(obj)).toBe(false); expect(_.not('b')(obj)).toBe(true); expect(_.not('c')(obj)).toBe(true); }); it('can select negative conditions on objects', function () { var obj = {a: 3, b: 6}; expect(_.not({a: 3})(obj)).toBe(false); expect(_.not({a: 4})(obj)).toBe(true); expect(_.not({a: 4, b: 6})(obj)).toBe(true); }); it('negates itself', function() { expect(function(fn, value) { return fn(value) === _.not(_.not(fn))(value); }).forAll(qc.function(qc.bool), qc.any); }); }); describe('_.clamp', function() { it('clamps values', function() { expect(function(a,b,c) { var v = _.clamp(a, b, c); return b <= c && v >= b && v <= c || b > c && v <= b && v >= c; }).forAll(qc.real, qc.real, qc.real); }); it('doesn\'t clamp if c is undefined', function() { expect(function(a,b,c) { return _.clamp(a, b, c) === a || _.clamp(a, b, c) === b; }).forAll(qc.real, qc.real, _.constant(undefined)); }); });