diff --git a/lodash.js b/lodash.js index b0bdabe4b..c96149ad7 100644 --- a/lodash.js +++ b/lodash.js @@ -710,35 +710,6 @@ return false; } - /** - * The base implementation of methods like `_.max` and `_.min` which accepts a - * `comparator` to determine the extremum value. - * - * @private - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per iteration. - * @param {Function} comparator The comparator used to compare values. - * @returns {*} Returns the extremum value. - */ - function baseExtremum(array, iteratee, comparator) { - var index = -1, - length = array.length; - - while (++index < length) { - var value = array[index], - current = iteratee(value); - - if (current != null && (computed === undefined - ? current === current - : comparator(current, computed) - )) { - var computed = current, - result = value; - } - } - return result; - } - /** * The base implementation of methods like `_.find` and `_.findKey`, without * support for iteratee shorthands, which iterates over `collection` using @@ -1017,79 +988,6 @@ return (value && value.Object === Object) ? value : null; } - /** - * Compares values to sort them in ascending order. - * - * @private - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {number} Returns the sort order indicator for `value`. - */ - function compareAscending(value, other) { - if (value !== other) { - var valIsNull = value === null, - valIsUndef = value === undefined, - valIsReflexive = value === value; - - var othIsNull = other === null, - othIsUndef = other === undefined, - othIsReflexive = other === other; - - if ((value > other && !othIsNull) || !valIsReflexive || - (valIsNull && !othIsUndef && othIsReflexive) || - (valIsUndef && othIsReflexive)) { - return 1; - } - if ((value < other && !valIsNull) || !othIsReflexive || - (othIsNull && !valIsUndef && valIsReflexive) || - (othIsUndef && valIsReflexive)) { - return -1; - } - } - return 0; - } - - /** - * Used by `_.orderBy` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, - * specify an order of "desc" for descending or "asc" for ascending sort order - * of corresponding values. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {boolean[]|string[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ - function compareMultiple(object, other, orders) { - var index = -1, - objCriteria = object.criteria, - othCriteria = other.criteria, - length = objCriteria.length, - ordersLength = orders.length; - - while (++index < length) { - var result = compareAscending(objCriteria[index], othCriteria[index]); - if (result) { - if (index >= ordersLength) { - return result; - } - var order = orders[index]; - return result * (order == 'desc' ? -1 : 1); - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. - return object.index - other.index; - } - /** * Gets the number of `placeholder` occurrences in `array`. * @@ -1110,29 +1008,6 @@ return result; } - /** - * Creates a function that performs a mathematical operation on two values. - * - * @private - * @param {Function} operator The function to perform the operation. - * @returns {Function} Returns the new mathematical operation function. - */ - function createMathOperation(operator) { - return function(value, other) { - var result; - if (value === undefined && other === undefined) { - return 0; - } - if (value !== undefined) { - result = value; - } - if (other !== undefined) { - result = result === undefined ? other : operator(result, other); - } - return result; - }; - } - /** * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. * @@ -2613,6 +2488,35 @@ return result; } + /** + * The base implementation of methods like `_.max` and `_.min` which accepts a + * `comparator` to determine the extremum value. + * + * @private + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per iteration. + * @param {Function} comparator The comparator used to compare values. + * @returns {*} Returns the extremum value. + */ + function baseExtremum(array, iteratee, comparator) { + var index = -1, + length = array.length; + + while (++index < length) { + var value = array[index], + current = iteratee(value); + + if (current != null && (computed === undefined + ? (current === current && !isSymbol(current)) + : comparator(current, computed) + )) { + var computed = current, + result = value; + } + } + return result; + } + /** * The base implementation of `_.fill` without an iteratee call guard. * @@ -2795,6 +2699,19 @@ : arrayPush(result, symbolsFunc(object)); } + /** + * The base implementation of `_.gt` which doesn't coerce arguments to numbers. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + */ + function baseGt(value, other) { + return value > other; + } + /** * The base implementation of `_.has` without support for deep paths. * @@ -3134,6 +3051,19 @@ }; } + /** + * The base implementation of `_.lt` which doesn't coerce arguments to numbers. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + */ + function baseLt(value, other) { + return value < other; + } + /** * The base implementation of `_.map` without support for iteratee shorthands. * @@ -3667,7 +3597,8 @@ var mid = (low + high) >>> 1, computed = array[mid]; - if ((retHighest ? (computed <= value) : (computed < value)) && computed !== null) { + if (computed !== null && !isSymbol(computed) && + (retHighest ? (computed <= value) : (computed < value))) { low = mid + 1; } else { high = mid; @@ -3698,21 +3629,26 @@ high = array ? array.length : 0, valIsNaN = value !== value, valIsNull = value === null, - valIsUndef = value === undefined; + valIsSymbol = isSymbol(value), + valIsUndefined = value === undefined; while (low < high) { var mid = nativeFloor((low + high) / 2), computed = iteratee(array[mid]), - isDef = computed !== undefined, - isReflexive = computed === computed; + othIsDefined = computed !== undefined, + othIsNull = computed === null, + othIsReflexive = computed === computed, + othIsSymbol = isSymbol(computed); if (valIsNaN) { - var setLow = isReflexive || retHighest; + var setLow = retHighest || othIsReflexive; + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined); } else if (valIsNull) { - setLow = isReflexive && isDef && (retHighest || computed != null); - } else if (valIsUndef) { - setLow = isReflexive && (retHighest || isDef); - } else if (computed == null) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull); + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol); + } else if (othIsNull || othIsSymbol) { setLow = false; } else { setLow = retHighest ? (computed <= value) : (computed < value); @@ -3767,6 +3703,27 @@ return result; } + function baseToNumber(value) { + if (typeof value == 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + return +value; + } + + function baseToString(value) { + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value == 'string') { + return value; + } + if (isSymbol(value)) { + return symbolToString ? symbolToString.call(value) : ''; + } + return (value + ''); + } + /** * The base implementation of `_.uniqBy` without support for iteratee shorthands. * @@ -4102,6 +4059,85 @@ return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length); } + /** + * Compares values to sort them in ascending order. + * + * @private + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {number} Returns the sort order indicator for `value`. + */ + function compareAscending(value, other) { + if (value !== other) { + var valIsNull = value === null, + valIsUndef = value === undefined, + valIsReflexive = value === value, + valIsSymbol = isSymbol(value); + + var othIsNull = other === null, + othIsUndef = other === undefined, + othIsReflexive = other === other, + othIsSymbol = isSymbol(other); + + if ((valIsSymbol && !othIsSymbol) || + (!othIsNull && !othIsSymbol && value > other) || + (valIsNull && !othIsUndef && othIsReflexive) || + (valIsUndef && othIsReflexive) || + !valIsReflexive) { + return 1; + } + if ((othIsSymbol && !valIsSymbol) || + (!valIsNull && !valIsSymbol && value < other) || + (othIsNull && !valIsUndef && valIsReflexive) || + (othIsUndef && valIsReflexive) || + !othIsReflexive) { + return -1; + } + } + return 0; + } + + /** + * Used by `_.orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {boolean[]|string[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ + function compareMultiple(object, other, orders) { + var index = -1, + objCriteria = object.criteria, + othCriteria = other.criteria, + length = objCriteria.length, + ordersLength = orders.length; + + while (++index < length) { + var result = compareAscending(objCriteria[index], othCriteria[index]); + if (result) { + if (index >= ordersLength) { + return result; + } + var order = orders[index]; + return result * (order == 'desc' ? -1 : 1); + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index; + } + /** * Creates an array that is the composition of partially applied arguments, * placeholders, and provided arguments into a single array of arguments. @@ -4621,6 +4657,39 @@ }; } + /** + * Creates a function that performs a mathematical operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new mathematical operation function. + */ + function createMathOperation(operator) { + return function(value, other) { + var result; + if (value === undefined && other === undefined) { + return 0; + } + if (value !== undefined) { + result = value; + } + if (other !== undefined) { + if (result === undefined) { + return other; + } + if (typeof value == 'string' || typeof other == 'string') { + value = baseToString(value); + other = baseToString(other); + } else { + value = baseToNumber(value); + other = baseToNumber(other); + } + result = operator(value, other); + } + return result; + }; + } + /** * Creates a function like `_.over`. * @@ -4727,6 +4796,23 @@ }; } + /** + * Creates a function that performs a relational operation on two values. + * + * @private + * @param {Function} operator The function to perform the operation. + * @returns {Function} Returns the new relational operation function. + */ + function createRelationalOperation(operator) { + return function(value, other) { + if (!(typeof value == 'string' && typeof other == 'string')) { + value = toNumber(value); + other = toNumber(other); + } + return operator(value, other); + }; + } + /** * Creates a function that wraps `func` to continue currying. * @@ -5544,7 +5630,7 @@ */ function isKey(value, object) { var type = typeof value; - if (type == 'number' || type == 'symbol') { + if (type == 'number' || type == 'boolean' || type == 'symbol' || value == null) { return true; } return !isArray(value) && @@ -5561,8 +5647,8 @@ */ function isKeyable(value) { var type = typeof value; - return type == 'number' || type == 'boolean' || - (type == 'string' && value != '__proto__') || value == null; + return type == 'number' || type == 'boolean' || type == 'symbol' || value == null || + (type == 'string' && value != '__proto__'); } /** @@ -5813,8 +5899,8 @@ * @param {*} value The value to inspect. * @returns {string|symbol} Returns the key. */ - function toKey(key) { - return (typeof key == 'string' || isSymbol(key)) ? key : (key + ''); + function toKey(value) { + return (typeof value == 'string' || isSymbol(value)) ? value : (value + ''); } /** @@ -6839,8 +6925,13 @@ var pullAt = rest(function(array, indexes) { indexes = baseFlatten(indexes, 1); - var result = baseAt(array, indexes); - basePullAt(array, indexes.sort(compareAscending)); + var length = array ? array.length : 0, + result = baseAt(array, indexes); + + basePullAt(array, arrayMap(indexes, function(index) { + return isIndex(index, length) ? +index : index; + }).sort(compareAscending)); + return result; }); @@ -10253,9 +10344,7 @@ * _.gt(1, 3); * // => false */ - function gt(value, other) { - return value > other; - } + var gt = createRelationalOperation(baseGt); /** * Checks if `value` is greater than or equal to `other`. @@ -10279,9 +10368,9 @@ * _.gte(1, 3); * // => false */ - function gte(value, other) { + var gte = createRelationalOperation(function(value, other) { return value >= other; - } + }); /** * Checks if `value` is likely an `arguments` object. @@ -11331,9 +11420,7 @@ * _.lt(3, 1); * // => false */ - function lt(value, other) { - return value < other; - } + var lt = createRelationalOperation(baseLt); /** * Checks if `value` is less than or equal to `other`. @@ -11357,9 +11444,9 @@ * _.lte(3, 1); * // => false */ - function lte(value, other) { + var lte = createRelationalOperation(function(value, other) { return value <= other; - } + }); /** * Converts `value` to an array. @@ -11497,8 +11584,9 @@ if (typeof value == 'number') { return value; } - if (isSymbol(value)) { - return NAN; + var result = baseToNumber(value); + if (result !== result) { + return result; } if (isObject(value)) { var other = isFunction(value.valueOf) ? value.valueOf() : value; @@ -11592,17 +11680,10 @@ * // => '1,2,3' */ function toString(value) { - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value == 'string') { - return value; - } if (value == null) { return ''; } - if (isSymbol(value)) { - return symbolToString ? symbolToString.call(value) : ''; - } - var result = (value + ''); + var result = baseToString(value); return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result; } @@ -15195,7 +15276,7 @@ */ function max(array) { return (array && array.length) - ? baseExtremum(array, identity, gt) + ? baseExtremum(array, identity, baseGt) : undefined; } @@ -15225,7 +15306,7 @@ */ function maxBy(array, iteratee) { return (array && array.length) - ? baseExtremum(array, getIteratee(iteratee), gt) + ? baseExtremum(array, getIteratee(iteratee), baseGt) : undefined; } @@ -15295,7 +15376,7 @@ */ function min(array) { return (array && array.length) - ? baseExtremum(array, identity, lt) + ? baseExtremum(array, identity, baseLt) : undefined; } @@ -15325,7 +15406,7 @@ */ function minBy(array, iteratee) { return (array && array.length) - ? baseExtremum(array, getIteratee(iteratee), lt) + ? baseExtremum(array, getIteratee(iteratee), baseLt) : undefined; }