diff --git a/lodash.js b/lodash.js index e97e21af2..bae836735 100644 --- a/lodash.js +++ b/lodash.js @@ -1790,11 +1790,11 @@ function baseFill(array, value, start, end) { var length = array.length; - start = start == null ? 0 : (+start || 0); + start = start == null ? 0 : toInteger(start); if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (end === undefined || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : toInteger(end); if (end < 0) { end += length; } @@ -2552,11 +2552,11 @@ var index = -1, length = array.length; - start = start == null ? 0 : (+start || 0); + start = start == null ? 0 : toInteger(start); if (start < 0) { start = -start > length ? 0 : (length + start); } - end = (end === undefined || end > length) ? length : (+end || 0); + end = (end === undefined || end > length) ? length : toInteger(end); if (end < 0) { end += length; } @@ -3387,7 +3387,7 @@ function createRound(methodName) { var func = Math[methodName]; return function(number, precision) { - precision = precision === undefined ? 0 : (+precision || 0); + precision = precision === undefined ? 0 : toInteger(precision); if (precision) { precision = pow(10, precision); return func(number * precision) / precision; @@ -3438,6 +3438,9 @@ partials = holders = undefined; } + ary = ary == null ? ary : nativeMax(toInteger(ary), 0); + arity = arity == null ? arity : toInteger(arity); + var data = isBindKey ? undefined : getData(func), newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity]; @@ -3448,7 +3451,7 @@ } newData[9] = arity == null ? (isBindKey ? 0 : func.length) - : (nativeMax(arity - length, 0) || 0); + : nativeMax(arity - length, 0); if (bitmask == BIND_FLAG) { var result = createBindWrapper(newData[0], newData[2]); @@ -4141,8 +4144,19 @@ * @param {*} value The value to process. * @returns {Function} Returns the function. */ - function toFunction(func) { - return typeof func == 'function' ? func : identity; + function toFunction(value) { + return typeof value == 'function' ? value : identity; + } + + /** + * Converts `value` to an integer. + * + * @private + * @param {*} value The value to convert. + * @returns {number} Returns the integer. + */ + function toInteger(value) { + return nativeFloor(value) || 0; } /** @@ -4198,7 +4212,7 @@ * // => [['a', 'b', 'c'], ['d']] */ function chunk(array, size) { - size = nativeMax(nativeFloor(size) || 0, 0); + size = nativeMax(toInteger(size), 0); var length = array ? array.length : 0; if (!length || size < 1) { @@ -4326,8 +4340,8 @@ if (!length) { return []; } - n = (guard || n == null) ? 1 : n; - n = length - (+n || 0); + n = (guard || n == null) ? 1 : toInteger(n); + n = length - n; return baseSlice(array, 0, n < 0 ? 0 : n); } @@ -4623,6 +4637,7 @@ return -1; } if (typeof fromIndex == 'number') { + fromIndex = toInteger(fromIndex); fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; } else if (fromIndex) { var index = binaryIndex(array, value); @@ -4753,7 +4768,8 @@ } var index = length; if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? nativeMax(length + fromIndex, 0) : nativeMin(fromIndex || 0, length - 1)) + 1; + index = toInteger(fromIndex); + index = (index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1)) + 1; } else if (fromIndex) { index = binaryIndex(array, value, true) - 1; var other = array[index]; @@ -5090,8 +5106,8 @@ if (!length) { return []; } - n = (guard || n == null) ? 1 : n; - n = length - (+n || 0); + n = (guard || n == null) ? 1 : toInteger(n); + n = length - n; return baseSlice(array, n < 0 ? 0 : n); } @@ -6055,7 +6071,8 @@ if (guard || typeof fromIndex != 'number') { fromIndex = 0; } else { - fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0); + fromIndex = toInteger(fromIndex); + fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : fromIndex; } return (typeof collection == 'string' || !isArray(collection) && isString(collection)) ? (fromIndex <= length && collection.indexOf(target, fromIndex) > -1) @@ -6357,7 +6374,7 @@ length = result.length, lastIndex = length - 1; - n = nativeMin(n < 0 ? 0 : (+n || 0), length); + n = nativeMin(n < 0 ? 0 : toInteger(n), length); while (++index < n) { var rand = baseRandom(index, lastIndex), value = result[rand]; @@ -6627,7 +6644,7 @@ */ function ary(func, n, guard) { n = guard ? undefined : n; - n = (func && n == null) ? func.length : nativeMax(+n || 0, 0); + n = (func && n == null) ? func.length : n; return createWrapper(func, ARY_FLAG, undefined, undefined, undefined, undefined, n); } @@ -7442,7 +7459,7 @@ if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } - start = nativeMax(start === undefined ? (func.length - 1) : (+start || 0), 0); + start = nativeMax(start === undefined ? (func.length - 1) : toInteger(start), 0); return function() { var args = arguments, index = -1, @@ -9742,7 +9759,7 @@ var length = string.length; position = position === undefined ? length - : nativeMin(position < 0 ? 0 : (+position || 0), length); + : nativeMin(position < 0 ? 0 : toInteger(position), length); position -= target.length; return position >= 0 && string.indexOf(target, position) == position; @@ -10069,7 +10086,7 @@ string = baseToString(string); position = position == null ? 0 - : nativeMin(position < 0 ? 0 : (+position || 0), string.length); + : nativeMin(position < 0 ? 0 : toInteger(position), string.length); return string.lastIndexOf(target, position) == position; } @@ -10412,7 +10429,7 @@ if (isObject(options)) { var separator = 'separator' in options ? options.separator : separator; - length = 'length' in options ? (+options.length || 0) : length; + length = 'length' in options ? toInteger(options.length) : length; omission = 'omission' in options ? baseToString(options.omission) : omission; } string = baseToString(string); @@ -11549,7 +11566,7 @@ if (filtered && !index) { return new LazyWrapper(this); } - n = n == null ? 1 : nativeMax(nativeFloor(n) || 0, 0); + n = n == null ? 1 : nativeMax(toInteger(n), 0); var result = this.clone(); if (filtered) { @@ -11616,7 +11633,7 @@ }; LazyWrapper.prototype.slice = function(start, end) { - start = start == null ? 0 : (+start || 0); + start = start == null ? 0 : toInteger(start); var result = this; if (result.__filtered__ && (start > 0 || end < 0)) { @@ -11628,7 +11645,7 @@ result = result.drop(start); } if (end !== undefined) { - end = (+end || 0); + end = toInteger(end); result = end < 0 ? result.dropRight(-end) : result.take(end - start); } return result; diff --git a/test/test.js b/test/test.js index fb891faeb..d2add8bc1 100644 --- a/test/test.js +++ b/test/test.js @@ -943,6 +943,18 @@ deepEqual(actual, []); }); + test('should coerce `n` to an integer', 1, function() { + var values = ['1', 1.6, 'xyz'], + expected = [['a'], ['a'], []]; + + var actual = _.map(values, function(n) { + var capped = _.ary(fn, n); + return capped('a', 'b'); + }); + + deepEqual(actual, expected); + }); + test('should work when provided less than the capped numer of arguments', 1, function() { var capped = _.ary(fn, 3); deepEqual(capped('a'), ['a']); @@ -1793,7 +1805,7 @@ deepEqual(actual, expected); }); - test('should floor `size` values', 1, function() { + test('should coerce `size` to an integer', 1, function() { deepEqual(_.chunk(array, array.length / 4), [[0], [1], [2], [3], [4], [5]]); }); }()); @@ -2445,8 +2457,8 @@ deepEqual(curried(1, 2, 3), expected); }); - test('should coerce `arity` to a number', 2, function() { - var values = ['0', 'xyz'], + test('should coerce `arity` to an integer', 2, function() { + var values = ['0', 0.6, 'xyz'], expected = _.map(values, _.constant([])); var actual = _.map(values, function(arity) { @@ -2467,19 +2479,6 @@ deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3, 4]); }); - test('should work with partialed methods', 2, function() { - var curried = _.curry(fn), - expected = [1, 2, 3, 4]; - - var a = _.partial(curried, 1), - b = _.bind(a, null, 2), - c = _.partialRight(b, 4), - d = _.partialRight(b(3), 4); - - deepEqual(c(3), expected); - deepEqual(d(), expected); - }); - test('should provide additional arguments after reaching the target arity', 3, function() { var curried = _.curry(fn, 3); deepEqual(curried(1)(2, 3, 4), [1, 2, 3, 4]); @@ -2530,6 +2529,19 @@ deepEqual(object.curried('a', 'b')('c'), Array(3)); deepEqual(object.curried('a', 'b', 'c'), expected); }); + + test('should work with partialed methods', 2, function() { + var curried = _.curry(fn), + expected = [1, 2, 3, 4]; + + var a = _.partial(curried, 1), + b = _.bind(a, null, 2), + c = _.partialRight(b, 4), + d = _.partialRight(b(3), 4); + + deepEqual(c(3), expected); + deepEqual(d(), expected); + }); }()); /*--------------------------------------------------------------------------*/ @@ -2559,17 +2571,16 @@ deepEqual(curried(1, 2, 3), expected); }); - test('should work with partialed methods', 2, function() { - var curried = _.curryRight(fn), - expected = [1, 2, 3, 4]; + test('should coerce `arity` to an integer', 2, function() { + var values = ['0', 0.6, 'xyz'], + expected = _.map(values, _.constant([])); - var a = _.partialRight(curried, 4), - b = _.partialRight(a, 3), - c = _.bind(b, null, 1), - d = _.partial(b(2), 1); + var actual = _.map(values, function(arity) { + return _.curryRight(fn, arity)(); + }); - deepEqual(c(2), expected); - deepEqual(d(), expected); + deepEqual(actual, expected); + deepEqual(_.curryRight(fn, '2')(1)(2), [2, 1]); }); test('should support placeholders', 4, function() { @@ -2633,6 +2644,19 @@ deepEqual(object.curried('b', 'c')('a'), Array(3)); deepEqual(object.curried('a', 'b', 'c'), expected); }); + + test('should work with partialed methods', 2, function() { + var curried = _.curryRight(fn), + expected = [1, 2, 3, 4]; + + var a = _.partialRight(curried, 4), + b = _.partialRight(a, 3), + c = _.bind(b, null, 1), + d = _.partial(b(2), 1); + + deepEqual(c(2), expected); + deepEqual(d(), expected); + }); }()); /*--------------------------------------------------------------------------*/ @@ -3226,6 +3250,10 @@ }); }); + test('should coerce `n` to an integer', 1, function() { + deepEqual(_.drop(array, 1.2), [2, 3]); + }); + test('should work as an iteratee for methods like `_.map`', 1, function() { var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], actual = _.map(array, _.drop); @@ -3296,6 +3324,10 @@ }); }); + test('should coerce `n` to an integer', 1, function() { + deepEqual(_.dropRight(array, 1.2), [1, 2]); + }); + test('should work as an iteratee for methods like `_.map`', 1, function() { var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], actual = _.map(array, _.dropRight); @@ -3510,6 +3542,10 @@ }); }); + test('should coerce `position` to an integer', 1, function() { + strictEqual(_.endsWith(string, 'ab', 2.2), true); + }); + test('should return `true` when `target` is an empty string regardless of `position`', 1, function() { ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity], function(position) { return _.endsWith(string, '', position, true); @@ -3818,7 +3854,7 @@ }); test('should coerce `start` and `end` to integers', 1, function() { - var positions = [[0.1, 1.1], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; + var positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; var actual = _.map(positions, function(pos) { var array = [1, 2, 3]; @@ -5560,6 +5596,10 @@ }); }); + test('should work with ' + key + ' and floor `position` values', 1, function() { + strictEqual(_.includes(collection, 2, 1.2), true); + }); + test('should work with ' + key + ' and return an unwrapped value implicitly when chaining', 1, function() { if (!isNpm) { strictEqual(_(collection).includes(3), true); @@ -5721,28 +5761,6 @@ deepEqual(actual, expected); }); - test('should treat falsey `fromIndex` values as `0`', 1, function() { - var expected = _.map(falsey, _.constant(0)); - - var actual = _.map(falsey, function(fromIndex) { - return _.indexOf(array, 1, fromIndex); - }); - - deepEqual(actual, expected); - }); - - test('should perform a binary search when `fromIndex` is a non-number truthy value', 1, function() { - var sorted = [4, 4, 5, 5, 6, 6], - values = [true, '1', {}], - expected = _.map(values, _.constant(2)); - - var actual = _.map(values, function(value) { - return _.indexOf(sorted, 5, value); - }); - - deepEqual(actual, expected); - }); - test('should work with a negative `fromIndex`', 1, function() { strictEqual(_.indexOf(array, 2, -3), 4); }); @@ -5757,6 +5775,32 @@ deepEqual(actual, expected); }); + + test('should treat falsey `fromIndex` values as `0`', 1, function() { + var expected = _.map(falsey, _.constant(0)); + + var actual = _.map(falsey, function(fromIndex) { + return _.indexOf(array, 1, fromIndex); + }); + + deepEqual(actual, expected); + }); + + test('should coerce `fromIndex` to an integer', 1, function() { + strictEqual(_.indexOf(array, 2, 1.2), 1); + }); + + test('should perform a binary search when `fromIndex` is a non-number truthy value', 1, function() { + var sorted = [4, 4, 5, 5, 6, 6], + values = [true, '1', {}], + expected = _.map(values, _.constant(2)); + + var actual = _.map(values, function(value) { + return _.indexOf(sorted, 5, value); + }); + + deepEqual(actual, expected); + }); }()); /*--------------------------------------------------------------------------*/ @@ -8926,6 +8970,21 @@ deepEqual(actual, expected); }); + test('should work with a negative `fromIndex`', 1, function() { + strictEqual(_.lastIndexOf(array, 2, -3), 1); + }); + + test('should work with a negative `fromIndex` <= `-array.length`', 1, function() { + var values = [-6, -8, -Infinity], + expected = _.map(values, _.constant(0)); + + var actual = _.map(values, function(fromIndex) { + return _.lastIndexOf(array, 1, fromIndex); + }); + + deepEqual(actual, expected); + }); + test('should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`', 1, function() { var expected = _.map(falsey, function(value) { return typeof value == 'number' ? -1 : 5; @@ -8938,6 +8997,10 @@ deepEqual(actual, expected); }); + test('should coerce `fromIndex` to an integer', 1, function() { + strictEqual(_.lastIndexOf(array, 2, 4.2), 4); + }); + test('should perform a binary search when `fromIndex` is a non-number truthy value', 1, function() { var sorted = [4, 4, 5, 5, 6, 6], values = [true, '1', {}], @@ -8949,21 +9012,6 @@ deepEqual(actual, expected); }); - - test('should work with a negative `fromIndex`', 1, function() { - strictEqual(_.lastIndexOf(array, 2, -3), 1); - }); - - test('should work with a negative `fromIndex` <= `-array.length`', 1, function() { - var values = [-6, -8, -Infinity], - expected = _.map(values, _.constant(0)); - - var actual = _.map(values, function(fromIndex) { - return _.lastIndexOf(array, 1, fromIndex); - }); - - deepEqual(actual, expected); - }); }()); /*--------------------------------------------------------------------------*/ @@ -13149,6 +13197,11 @@ deepEqual(actual, expected); }); + test('should coerce `start` to an integer', 1, function() { + var rp = _.restParam(fn, 1.6); + deepEqual(rp(1, 2, 3), [1, [2, 3]]) + }); + test('should use an empty array when `start` is not reached', 1, function() { var rp = _.restParam(fn); deepEqual(rp(1), [1, undefined, []]); @@ -13191,12 +13244,17 @@ strictEqual(actual, isCeil ? 5 : 4); }); - test('`_.' + methodName + '` should coerce `precision` values to numbers and `NaN` to `0`', 2, function() { + test('`_.' + methodName + '` should coerce `precision` to an integer', 3, function() { var actual = func(4.006, NaN); strictEqual(actual, isCeil ? 5 : 4); + var expected = isFloor ? 4.01 : 4.02; + + actual = func(4.016, 2.6); + strictEqual(actual, expected); + actual = func(4.016, '+2'); - strictEqual(actual, isFloor ? 4.01 : 4.02); + strictEqual(actual, expected); }); test('`_.' + methodName + '` should return a rounded number with a positive precision', 1, function() { @@ -13297,6 +13355,11 @@ }); }); + test('should coerce `n` to an integer', 1, function() { + var actual = _.sample(array, 1.6); + strictEqual(actual.length, 1); + }); + test('should return `undefined` when sampling an empty array', 1, function() { strictEqual(_.sample([]), undefined); }); @@ -13741,7 +13804,7 @@ }); test('should coerce `start` and `end` to integers', 1, function() { - var positions = [[0.1, 1.1], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; + var positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; var actual = _.map(positions, function(pos) { return _.slice.apply(_, [array].concat(pos)); @@ -14244,6 +14307,10 @@ }); }); + test('should coerce `position` to an integer', 1, function() { + strictEqual(_.startsWith(string, 'bc', 1.2), true); + }); + test('should return `true` when `target` is an empty string regardless of `position`', 1, function() { ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity], function(position) { return _.startsWith(string, '', position, true); @@ -14834,7 +14901,7 @@ }); test('should coerce `length` to an integer', 4, function() { - _.each(['', NaN, 4.5, '4'], function(length, index) { + _.each(['', NaN, 4.6, '4'], function(length, index) { var actual = index > 1 ? 'h...' : '...'; strictEqual(_.trunc(string, { 'length': { 'valueOf': _.constant(length) } }), actual); }); @@ -15275,7 +15342,7 @@ }); }); - test('should floor `n` float values', 1, function() { + test('should coerce `n` to an integer', 1, function() { var actual = _.times(2.4, _.indentify); deepEqual(actual, [0, 1]); });