Ensure many n like params are coerced to integers. [closes #1377]

This commit is contained in:
John-David Dalton
2015-07-31 08:35:44 -07:00
parent 7f7ebed4ea
commit 73464d6bb6
2 changed files with 176 additions and 92 deletions

View File

@@ -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;

View File

@@ -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]);
});