diff --git a/lodash.js b/lodash.js
index ec1a9a8d4..eae8c26cd 100644
--- a/lodash.js
+++ b/lodash.js
@@ -3,7 +3,7 @@
* Lo-Dash 2.4.1
* Copyright 2012-2014 The Dojo Foundation
* Based on Underscore.js 1.6.0
- * Copyright 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ * Copyright 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Available under MIT license
*/
;(function() {
@@ -25,9 +25,15 @@
/** Used as the property name for wrapper metadata */
var expando = '__lodash@' + version + '__';
+ /** Used as the TypeError message for "Functions" methods */
+ var funcErrorText = 'Expected a function';
+
/** Used to generate unique IDs */
var idCounter = 0;
+ /** Used to detect words composed of all capital letters */
+ var reAllCaps = /^[A-Z]+$/;
+
/** Used to match empty string literals in compiled template source */
var reEmptyStringLeading = /\b__p \+= '';/g,
reEmptyStringMiddle = /\b(__p \+=) '' \+/g,
@@ -43,12 +49,13 @@
reInterpolate = /<%=([\s\S]+?)%>/g;
/**
- * Used to match ES6 template delimiters
- * http://people.mozilla.org/~jorendorff/es6-draft.html#sec-literals-string-literals
+ * Used to match ES6 template delimiters.
+ * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components)
+ * for more details.
*/
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
- /** Used to match regexp flags from their coerced string values */
+ /** Used to match `RegExp` flags from their coerced string values */
var reFlags = /\w*$/;
/** Used to detected named functions */
@@ -57,14 +64,27 @@
/** Used to detect hexadecimal string values */
var reHexPrefix = /^0[xX]/;
+ /** Used to match latin-1 supplement letters */
+ var reLatin1 = /[\xC0-\xFF]/g;
+
/** Used to ensure capturing order of template delimiters */
var reNoMatch = /($^)/;
+ /**
+ * Used to match `RegExp` special characters.
+ * See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
+ * for more details.
+ */
+ var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g;
+
/** Used to detect functions containing a `this` reference */
var reThis = /\bthis\b/;
/** Used to match unescaped characters in compiled string literals */
- var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g;
+ var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
+
+ /** Used to match words to create compound words */
+ var reWords = /[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[a-z]+|[0-9]+/g;
/** Used to detect and test whitespace */
var whitespace = (
@@ -85,13 +105,13 @@
'parseInt', 'setTimeout', 'TypeError', 'window', 'WinRTError'
];
- /** Used to fix the JScript [[DontEnum]] bug */
+ /** Used to fix the JScript `[[DontEnum]]` bug */
var shadowedProps = [
'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable',
'toLocaleString', 'toString', 'valueOf'
];
- /** Used to make template sourceURLs easier to identify */
+ /** Used to make template `sourceURL`s easier to identify */
var templateCounter = 0;
/** `Object#toString` result shortcuts */
@@ -114,7 +134,7 @@
cloneableClasses[numberClass] = cloneableClasses[objectClass] =
cloneableClasses[regexpClass] = cloneableClasses[stringClass] = true;
- /** Used as an internal `_.debounce` options object */
+ /** Used as an internal `_.debounce` options object by `_.throttle` */
var debounceOptions = {
'leading': false,
'maxWait': 0,
@@ -155,7 +175,32 @@
''': "'"
};
- /** Used to determine if values are of the language type Object */
+ /**
+ * Used to convert latin-1 supplement letters to basic latin (ASCII) letters.
+ * See [Wikipedia](http://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
+ * for more details.
+ */
+ var deburredLetters = {
+ '\xC0': 'A', '\xC1': 'A', '\xC2': 'A', '\xC3': 'A', '\xC4': 'A', '\xC5': 'A',
+ '\xE0': 'a', '\xE1': 'a', '\xE2': 'a', '\xE3': 'a', '\xE4': 'a', '\xE5': 'a',
+ '\xC7': 'C', '\xE7': 'c',
+ '\xD0': 'D', '\xF0': 'd',
+ '\xC8': 'E', '\xC9': 'E', '\xCA': 'E', '\xCB': 'E',
+ '\xE8': 'e', '\xE9': 'e', '\xEA': 'e', '\xEB': 'e',
+ '\xCC': 'I', '\xCD': 'I', '\xCE': 'I', '\xCF': 'I',
+ '\xEC': 'i', '\xED': 'i', '\xEE': 'i', '\xEF': 'i',
+ '\xD1': 'N', '\xF1': 'n',
+ '\xD2': 'O', '\xD3': 'O', '\xD4': 'O', '\xD5': 'O', '\xD6': 'O', '\xD8': 'O',
+ '\xF2': 'o', '\xF3': 'o', '\xF4': 'o', '\xF5': 'o', '\xF6': 'o', '\xF8': 'o',
+ '\xD9': 'U', '\xDA': 'U', '\xDB': 'U', '\xDC': 'U',
+ '\xF9': 'u', '\xFA': 'u', '\xFB': 'u', '\xFC': 'u',
+ '\xDD': 'Y', '\xFD': 'y', '\xFF': 'y',
+ '\xC6': 'AE', '\xE6': 'ae',
+ '\xDE': 'Th', '\xFE': 'th',
+ '\xDF': 'ss', '\xD7': ' ', '\xF7': ' '
+ };
+
+ /** Used to determine if values are of the language type `Object` */
var objectTypes = {
'function': true,
'object': true
@@ -167,7 +212,6 @@
"'": "'",
'\n': 'n',
'\r': 'r',
- '\t': 't',
'\u2028': 'u2028',
'\u2029': 'u2029'
};
@@ -192,21 +236,33 @@
/*--------------------------------------------------------------------------*/
+ /**
+ * Used by `_.defaults` to customize its `_.assign` use.
+ *
+ * @private
+ * @param {*} objectValue The destination object property value.
+ * @param {*} sourceValue The source object property value.
+ * @returns {*} Returns the value to assign to the destination object.
+ */
+ function assignDefaults(objectValue, sourceValue) {
+ return typeof objectValue == 'undefined' ? sourceValue : objectValue;
+ }
+
/**
* The base implementation of `compareAscending` used to compare values and
* sort them in ascending order without guaranteeing a stable sort.
*
* @private
- * @param {*} a The value to compare to `b`.
- * @param {*} b The value to compare to `a`.
- * @returns {number} Returns the sort order indicator for `a`.
+ * @param {*} value The value to compare to `other`.
+ * @param {*} other The value to compare to `value`.
+ * @returns {number} Returns the sort order indicator for `value`.
*/
- function baseCompareAscending(a, b) {
- if (a !== b) {
- if (a > b || typeof a == 'undefined') {
+ function baseCompareAscending(value, other) {
+ if (value !== other) {
+ if (value > other || typeof value == 'undefined') {
return 1;
}
- if (a < b || typeof b == 'undefined') {
+ if (value < other || typeof other == 'undefined') {
return -1;
}
}
@@ -220,7 +276,7 @@
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {number} [fromIndex=0] The index to search from.
- * @returns {number} Returns the index of the matched value or `-1`.
+ * @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
@@ -248,22 +304,24 @@
}
/**
- * Used by `_.max` and `_.min` as the default callback when a given
- * collection is a string value.
- *
- * @private
- * @param {string} value The character to inspect.
- * @returns {number} Returns the code unit of given character.
- */
- function charAtCallback(value) {
- return value.charCodeAt(0);
- }
-
- /**
- * Gets the index of the first character of `string` that is not found in `chars`.
+ * Used by `_.max` and `_.min` as the default callback when a given collection
+ * is a string value.
*
* @private
* @param {string} string The string to inspect.
+ * @returns {number} Returns the code unit of the first character of the string.
+ */
+ function charAtCallback(string) {
+ return string.charCodeAt(0);
+ }
+
+ /**
+ * Used by `_.trim` and `_.trimLeft` to get the index of the first character
+ * of `string` that is not found in `chars`.
+ *
+ * @private
+ * @param {string} string The string to inspect.
+ * @param {string} chars The characters to find.
* @returns {number} Returns the index of the first character not found in `chars`.
*/
function charsLeftIndex(string, chars) {
@@ -279,10 +337,12 @@
}
/**
- * Gets the index of the last character of `string` that is not found in `chars`.
+ * Used by `_.trim` and `_.trimRight` to get the index of the last character
+ * of `string` that is not found in `chars`.
*
* @private
* @param {string} string The string to inspect.
+ * @param {string} chars The characters to find.
* @returns {number} Returns the index of the last character not found in `chars`.
*/
function charsRightIndex(string, chars) {
@@ -296,69 +356,103 @@
}
/**
- * Used by `sortBy` to compare transformed elements of a collection and stable
+ * Used by `_.sortBy` to compare transformed elements of a collection and stable
* sort them in ascending order.
*
* @private
- * @param {Object} a The object to compare to `b`.
- * @param {Object} b The object to compare to `a`.
- * @returns {number} Returns the sort order indicator for `a`.
+ * @param {Object} object The object to compare to `other`.
+ * @param {Object} other The object to compare to `object`.
+ * @returns {number} Returns the sort order indicator for `object`.
*/
- function compareAscending(a, b) {
- return baseCompareAscending(a.criteria, b.criteria) || a.index - b.index;
+ function compareAscending(object, other) {
+ return baseCompareAscending(object.criteria, other.criteria) || object.index - other.index;
}
/**
- * Used by `sortBy` to compare multiple properties of each element in a
+ * Used by `_.sortBy` to compare multiple properties of each element in a
* collection and stable sort them in ascending order.
*
* @private
- * @param {Object} a The object to compare to `b`.
- * @param {Object} b The object to compare to `a`.
- * @returns {number} Returns the sort order indicator for `a`.
+ * @param {Object} object The object to compare to `other`.
+ * @param {Object} other The object to compare to `object`.
+ * @returns {number} Returns the sort order indicator for `object`.
*/
- function compareMultipleAscending(a, b) {
- var ac = a.criteria,
- bc = b.criteria,
- index = -1,
- length = ac.length;
+ function compareMultipleAscending(object, other) {
+ var index = -1,
+ objCriteria = object.criteria,
+ othCriteria = other.criteria,
+ length = objCriteria.length;
while (++index < length) {
- var result = baseCompareAscending(ac[index], bc[index]);
+ var result = baseCompareAscending(objCriteria[index], othCriteria[index]);
if (result) {
return result;
}
}
// Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications
- // that causes it, under certain circumstances, to provided the same value
- // for `a` and `b`. See https://github.com/jashkenas/underscore/pull/1247
+ // that causes it, under certain circumstances, to provide the same value
+ // for `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247
//
// This also ensures a stable sort in V8 and other engines.
// See https://code.google.com/p/v8/issues/detail?id=90
- return a.index - b.index;
+ return object.index - other.index;
}
/**
- * Used by `escape` to convert characters to HTML entities.
+ * Creates a function that produces compound words out of the words in a
+ * given string.
*
* @private
- * @param {string} match The matched character to escape.
- * @returns {string} Returns the escaped character.
+ * @param {Function} callback The function called to combine each word.
+ * @returns {Function} Returns the new compounder function.
*/
- function escapeHtmlChar(match) {
- return htmlEscapes[match];
+ function createCompounder(callback) {
+ return function(string) {
+ var index = -1,
+ words = string != null && String(string).replace(reLatin1, deburrLetter).match(reWords),
+ length = words ? words.length : 0,
+ result = '';
+
+ while (++index < length) {
+ result = callback(result, words[index], index, words);
+ }
+ return result;
+ };
}
/**
- * Used by `template` to escape characters for inclusion in compiled
+ * Used by `createCompounder` to convert latin-1 supplement letters to basic
+ * latin (ASCII) letters.
+ *
+ * @private
+ * @param {string} letter The matched letter to deburr.
+ * @returns {string} Returns the deburred letter.
+ */
+ function deburrLetter(letter) {
+ return deburredLetters[letter];
+ }
+
+ /**
+ * Used by `_.escape` to convert characters to HTML entities.
+ *
+ * @private
+ * @param {string} chr The matched character to escape.
+ * @returns {string} Returns the escaped character.
+ */
+ function escapeHtmlChar(chr) {
+ return htmlEscapes[chr];
+ }
+
+ /**
+ * Used by `_.template` to escape characters for inclusion in compiled
* string literals.
*
* @private
- * @param {string} match The matched character to escape.
+ * @param {string} chr The matched character to escape.
* @returns {string} Returns the escaped character.
*/
- function escapeStringChar(match) {
- return '\\' + stringEscapes[match];
+ function escapeStringChar(chr) {
+ return '\\' + stringEscapes[chr];
}
/**
@@ -366,7 +460,7 @@
*
* @private
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a DOM node, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a DOM node, else `false`.
*/
function isNode(value) {
// IE < 9 presents DOM nodes as `Object` objects except they have `toString`
@@ -375,70 +469,8 @@
}
/**
- * A fallback implementation of `trim` to remove leading and trailing
- * whitespace or specified characters from `string`.
- *
- * @private
- * @param {string} string The string to trim.
- * @param {string} [chars=whitespace] The characters to trim.
- * @returns {string} Returns the trimmed string.
- */
- function shimTrim(string, chars) {
- string = string == null ? '' : String(string);
- if (!string) {
- return string;
- }
- if (chars == null) {
- return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1);
- }
- chars = String(chars);
- return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1);
- }
-
- /**
- * A fallback implementation of `trimLeft` to remove leading whitespace or
- * specified characters from `string`.
- *
- * @private
- * @param {string} string The string to trim.
- * @param {string} [chars=whitespace] The characters to trim.
- * @returns {string} Returns the trimmed string.
- */
- function shimTrimLeft(string, chars) {
- string = string == null ? '' : String(string);
- if (!string) {
- return string;
- }
- if (chars == null) {
- return string.slice(trimmedLeftIndex(string))
- }
- chars = String(chars);
- return string.slice(charsLeftIndex(string, chars));
- }
-
- /**
- * A fallback implementation of `trimRight` to remove trailing whitespace or
- * specified characters from `string`.
- *
- * @private
- * @param {string} string The string to trim.
- * @param {string} [chars=whitespace] The characters to trim.
- * @returns {string} Returns the trimmed string.
- */
- function shimTrimRight(string, chars) {
- string = string == null ? '' : String(string);
- if (!string) {
- return string;
- }
- if (chars == null) {
- return string.slice(0, trimmedRightIndex(string) + 1)
- }
- chars = String(chars);
- return string.slice(0, charsRightIndex(string, chars) + 1);
- }
-
- /**
- * Gets the index of the first non-whitespace character of `string`.
+ * Used by `_.trim` and `_.trimLeft` to get the index of the first non-whitespace
+ * character of `string`.
*
* @private
* @param {string} string The string to inspect.
@@ -459,7 +491,8 @@
}
/**
- * Gets the index of the last non-whitespace character of `string`.
+ * Used by `_.trim` and `_.trimRight` to get the index of the last non-whitespace
+ * character of `string`.
*
* @private
* @param {string} string The string to inspect.
@@ -478,20 +511,20 @@
}
/**
- * Used by `unescape` to convert HTML entities to characters.
+ * Used by `_.unescape` to convert HTML entities to characters.
*
* @private
- * @param {string} match The matched character to unescape.
+ * @param {string} chr The matched character to unescape.
* @returns {string} Returns the unescaped character.
*/
- function unescapeHtmlChar(match) {
- return htmlUnescapes[match];
+ function unescapeHtmlChar(chr) {
+ return htmlUnescapes[chr];
}
/*--------------------------------------------------------------------------*/
/**
- * Create a new `lodash` function using the given context object.
+ * Create a new `lodash` function using the given `context` object.
*
* @static
* @memberOf _
@@ -528,17 +561,23 @@
/** Used to detect DOM support */
var document = (document = context.window) && document.document;
- /** Used to restore the original `_` reference in `noConflict` */
+ /** Used to restore the original `_` reference in `_.noConflict` */
var oldDash = context._;
- /** Used to resolve the internal [[Class]] of values */
+ /**
+ * Used as the maximum length of an array-like object.
+ * See the [ES6 spec](http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength)
+ * for more details.
+ */
+ var maxSafeInteger = Math.pow(2, 53) - 1;
+
+ /** Used to resolve the internal `[[Class]]` of values */
var toString = objectProto.toString;
/** Used to detect if a method is native */
var reNative = RegExp('^' +
- String(toString)
- .replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
- .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+ escapeRegExp(toString)
+ .replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);
/** Native method shortcuts */
@@ -555,7 +594,7 @@
splice = arrayRef.splice,
unshift = arrayRef.unshift;
- /** Used to set meta data on functions */
+ /** Used to set metadata on functions */
var defineProperty = (function() {
// IE 8 only accepts DOM elements
try {
@@ -577,12 +616,9 @@
nativeMin = Math.min,
nativeNow = isNative(nativeNow = Date.now) && nativeNow,
nativeParseInt = context.parseInt,
- nativeRandom = Math.random,
- nativeTrim = isNative(nativeTrim = stringProto.trim) && !nativeTrim.call(whitespace) && nativeTrim,
- nativeTrimLeft = isNative(nativeTrimLeft = stringProto.trimLeft) && !nativeTrimLeft.call(whitespace) && nativeTrimLeft,
- nativeTrimRight = isNative(nativeTrimRight = stringProto.trimRight) && !nativeTrimRight.call(whitespace) && nativeTrimRight;
+ nativeRandom = Math.random;
- /** Used to lookup a built-in constructor by [[Class]] */
+ /** Used to lookup built-in constructors by `[[Class]]` */
var ctorByClass = {};
ctorByClass[arrayClass] = Array;
ctorByClass[boolClass] = Boolean;
@@ -593,7 +629,7 @@
ctorByClass[regexpClass] = RegExp;
ctorByClass[stringClass] = String;
- /** Used to avoid iterating non-enumerable properties in IE < 9 */
+ /** Used to avoid iterating over non-enumerable properties in IE < 9 */
var nonEnumProps = {};
nonEnumProps[arrayClass] = nonEnumProps[dateClass] = nonEnumProps[numberClass] = { 'constructor': true, 'toLocaleString': true, 'toString': true, 'valueOf': true };
nonEnumProps[boolClass] = nonEnumProps[stringClass] = { 'constructor': true, 'toString': true, 'valueOf': true };
@@ -626,18 +662,18 @@
* implicitly or explicitly included in the build.
*
* The chainable wrapper functions are:
- * `after`, `assign`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
+ * `after`, `assign`, `at`, `bind`, `bindAll`, `bindKey`, `chain`, `compact`,
* `compose`, `concat`, `constant`, `countBy`, `create`, `createCallback`,
* `curry`, `debounce`, `defaults`, `defer`, `delay`, `difference`, `filter`,
* `flatten`, `forEach`, `forEachRight`, `forIn`, `forInRight`, `forOwn`,
* `forOwnRight`, `functions`, `groupBy`, `indexBy`, `initial`, `intersection`,
* `invert`, `invoke`, `keys`, `map`, `mapValues`, `matches`, `max`, `memoize`,
- * `merge`, `min`, `noop`, `object`, `omit`, `once`, `pairs`, `partial`,
- * `partialRight`, `pick`, `pluck`, `property`, `pull`, `push`, `range`,
- * `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`,
- * `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`, `union`,
- * `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`, `xor`,
- * and `zip`
+ * `merge`, `min`, `mixin`, `noop`, `object`, `omit`, `once`, `pairs`, `partial`,
+ * `partialRight`, `pick`, `pluck`, `property`, `pull`, `pullAt`, `push`,
+ * `range`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
+ * `sortBy`, `splice`, `tap`, `throttle`, `times`, `toArray`, `transform`,
+ * `union`, `uniq`, `unshift`, `unzip`, `values`, `where`, `without`, `wrap`,
+ * `xor`, and `zip`
*
* The non-chainable wrapper functions are:
* `capitalize`, `clone`, `cloneDeep`, `contains`, `escape`, `every`, `find`,
@@ -645,10 +681,10 @@
* `identity`, `indexOf`, `isArguments`, `isArray`, `isBoolean`, `isDate`,
* `isElement`, `isEmpty`, `isEqual`, `isFinite`, `isFunction`, `isNaN`,
* `isNull`, `isNumber`, `isObject`, `isPlainObject`, `isRegExp`, `isString`,
- * `isUndefined`, `join`, `lastIndexOf`, `mixin`, `noConflict`, `now`,
- * `parseInt`, `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`,
- * `size`, `some`, `sortedIndex`, `runInContext`, `template`, `trim`,
- * `trimLeft`, `trimRight`, `unescape`, `uniqueId`, and `value`
+ * `isUndefined`, `join`, `lastIndexOf`, `noConflict`, `now`, `parseInt`,
+ * `pop`, `random`, `reduce`, `reduceRight`, `result`, `shift`, `size`, `some`,
+ * `sortedIndex`, `runInContext`, `template`, `trim`, `trimLeft`, `trimRight`,
+ * `unescape`, `uniqueId`, and `value`
*
* The wrapper functions `first`, `last`, and `sample` return wrapped values
* when `n` is provided, otherwise they return unwrapped values.
@@ -693,7 +729,7 @@
*
* @private
* @param {*} value The value to wrap in a `lodash` instance.
- * @param {boolean} [chainAll=false] A flag to enable chaining for all methods
+ * @param {boolean} [chainAll=false] A flag to enable chaining for all methods.
* @returns {Object} Returns a `lodash` instance.
*/
function lodashWrapper(value, chainAll) {
@@ -712,17 +748,19 @@
*/
var support = lodash.support = {};
- (function() {
+ (function(x) {
var ctor = function() { this.x = 1; },
object = { '0': 1, 'length': 1 },
props = [];
ctor.prototype = { 'valueOf': 1, 'y': 1 };
for (var key in new ctor) { props.push(key); }
- for (key in arguments) { }
+ for (var argsKey in arguments) { }
+ for (var strKey in 'x') { }
/**
- * Detect if an `arguments` object's [[Class]] is resolvable (all but Firefox < 4, IE < 9).
+ * Detect if the `[[Class]]` of `arguments` objects is resolvable
+ * (all but Firefox < 4, IE < 9).
*
* @memberOf _.support
* @type boolean
@@ -730,7 +768,8 @@
support.argsClass = toString.call(arguments) == argsClass;
/**
- * Detect if `arguments` objects are `Object` objects (all but Narwhal and Opera < 10.5).
+ * Detect if `arguments` objects are `Object` objects
+ * (all but Narwhal and Opera < 10.5).
*
* @memberOf _.support
* @type boolean
@@ -739,20 +778,21 @@
/**
* Detect if `name` or `message` properties of `Error.prototype` are
- * enumerable by default. (IE < 9, Safari < 5.1)
+ * enumerable by default (IE < 9, Safari < 5.1).
*
* @memberOf _.support
* @type boolean
*/
- support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') || propertyIsEnumerable.call(errorProto, 'name');
+ support.enumErrorProps = propertyIsEnumerable.call(errorProto, 'message') ||
+ propertyIsEnumerable.call(errorProto, 'name');
/**
* Detect if `prototype` properties are enumerable by default.
*
* Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1
* (if the prototype or a property on the prototype has been set)
- * incorrectly sets a function's `prototype` property [[Enumerable]]
- * value to `true`.
+ * incorrectly sets the `[[Enumerable]]` value of a function's `prototype`
+ * property to `true`.
*
* @memberOf _.support
* @type boolean
@@ -761,7 +801,8 @@
/**
* Detect if functions can be decompiled by `Function#toString`
- * (all but PS3 and older Opera mobile browsers & avoided in Windows 8 apps).
+ * (all but Firefox OS certified apps, older Opera mobile browsers, and
+ * the PlayStation 3; forced `false` for Windows 8 apps).
*
* @memberOf _.support
* @type boolean
@@ -777,19 +818,20 @@
support.funcNames = typeof Function.name == 'string';
/**
- * Detect if `arguments` object indexes are non-enumerable
- * (Firefox < 4, IE < 9, PhantomJS, Safari < 5.1).
+ * Detect if string indexes are non-enumerable
+ * (IE < 9, RingoJS, Rhino, Narwhal).
*
* @memberOf _.support
* @type boolean
*/
- support.nonEnumArgs = key != 0;
+ support.nonEnumStrings = strKey != '0';
/**
- * Detect if properties shadowing those on `Object.prototype` are non-enumerable.
+ * Detect if properties shadowing those on `Object.prototype` are
+ * non-enumerable.
*
- * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
- * made non-enumerable as well (a.k.a the JScript [[DontEnum]] bug).
+ * In IE < 9 an object's own properties, shadowing non-enumerable ones,
+ * are made non-enumerable as well (a.k.a the JScript `[[DontEnum]]` bug).
*
* @memberOf _.support
* @type boolean
@@ -797,7 +839,8 @@
support.nonEnumShadows = !/valueOf/.test(props);
/**
- * Detect if own properties are iterated after inherited properties (all but IE < 9).
+ * Detect if own properties are iterated after inherited properties
+ * (all but IE < 9).
*
* @memberOf _.support
* @type boolean
@@ -805,13 +848,15 @@
support.ownLast = props[0] != 'x';
/**
- * Detect if `Array#shift` and `Array#splice` augment array-like objects correctly.
+ * Detect if `Array#shift` and `Array#splice` augment array-like objects
+ * correctly.
*
* Firefox < 10, IE compatibility mode, and IE < 9 have buggy Array `shift()`
* and `splice()` functions that fail to remove the last element, `value[0]`,
* of array-like objects even though the `length` property is set to `0`.
* The `shift()` method is buggy in IE 8 compatibility mode, while `splice()`
- * is buggy regardless of mode in IE < 9 and buggy in compatibility mode in IE 9.
+ * is buggy regardless of mode in IE < 9 and buggy in compatibility mode
+ * in IE 9.
*
* @memberOf _.support
* @type boolean
@@ -821,8 +866,8 @@
/**
* Detect lack of support for accessing string characters by index.
*
- * IE < 8 can't access characters by index and IE 8 can only access
- * characters by index on string literals.
+ * IE < 8 can't access characters by index. IE 8 can only access characters
+ * by index on string literals, not string objects.
*
* @memberOf _.support
* @type boolean
@@ -842,9 +887,9 @@
}
/**
- * Detect if a DOM node's [[Class]] is resolvable (all but IE < 9)
- * and that the JS engine errors when attempting to coerce an object to
- * a string without a `toString` function.
+ * Detect if the `[[Class]]` of DOM nodes is resolvable (all but IE < 9)
+ * and that the JS engine errors when attempting to coerce an object to a
+ * string without a `toString` function.
*
* @memberOf _.support
* @type boolean
@@ -854,12 +899,31 @@
} catch(e) {
support.nodeClass = true;
}
- }(1));
+
+ /**
+ * Detect if `arguments` object indexes are non-enumerable.
+ *
+ * In Firefox < 4, IE < 9, PhantomJS, and Safari < 5.1 `arguments` object
+ * indexes are non-enumerable. Chrome < 25 and Node.js < 0.11.0 treat
+ * `arguments` object indexes as non-enumerable and fail `hasOwnProperty`
+ * checks for indexes that exceed their function's formal parameters with
+ * associated values of `0`.
+ *
+ * @memberOf _.support
+ * @type boolean
+ */
+ try {
+ support.nonEnumArgs = !(argsKey == '1' && hasOwnProperty.call(arguments, argsKey) &&
+ propertyIsEnumerable.call(arguments, argsKey));
+ } catch(e) {
+ support.nonEnumArgs = true;
+ }
+ }(0, 0));
/**
- * By default, the template delimiters used by Lo-Dash are similar to those in
- * embedded Ruby (ERB). Change the following template settings to use alternative
- * delimiters.
+ * By default, the template delimiters used by Lo-Dash are similar to those
+ * in embedded Ruby (ERB). Change the following template settings to use
+ * alternative delimiters.
*
* @static
* @memberOf _
@@ -920,88 +984,68 @@
/*--------------------------------------------------------------------------*/
/**
- * The template used to create iterator functions.
+ * A specialized version of `_.forEach` for arrays without support for
+ * callback shorthands or `this` binding.
*
* @private
- * @param {Object} data The data object used to populate the text.
- * @returns {string} Returns the interpolated text.
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Array} Returns `array`.
*/
- var iteratorTemplate = template(
- // assign the `result` variable an initial value
- 'var result = <%= init %>;\n' +
+ function arrayEach(array, callback) {
+ var index = -1,
+ length = array ? array.length : 0;
- // exit early if the first argument is not an object
- "if (!isObject(object)) {\n" +
- ' return result;\n' +
- '}' +
+ while (++index < length) {
+ if (callback(array[index], index, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
- // add support for iterating over `arguments` objects if needed
- '<% if (support.nonEnumArgs) { %>\n' +
- 'var length = object.length;\n' +
- 'if (length && isArguments(object)) {\n' +
- ' key = -1;\n' +
- ' while (++key < length) {\n' +
- " key += '';\n" +
- ' <%= loop %>;\n' +
- ' }\n' +
- ' return result;\n' +
- '}' +
- '<% } %>' +
+ /**
+ * A specialized version of `_.forEachRight` for arrays without support for
+ * callback shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Array} Returns `array`.
+ */
+ function arrayEachRight(array, callback) {
+ var length = array ? array.length : 0;
+ while (length--) {
+ if (callback(array[length], length, array) === false) {
+ break;
+ }
+ }
+ return array;
+ }
- // avoid iterating over `prototype` properties in older Firefox, Opera, and Safari
- '<% if (support.enumPrototypes) { %>\n' +
- "var skipProto = typeof object == 'function';\n" +
- '<% } %>' +
+ /**
+ * A specialized version of `_.map` for arrays without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Array} array The array to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Array} Returns the new mapped array.
+ */
+ function arrayMap(array, callback) {
+ var index = -1,
+ length = array ? array.length >>> 0 : 0,
+ result = Array(length);
- // avoid iterating over `Error.prototype` properties in older IE and Safari
- '<% if (support.enumErrorProps) { %>\n' +
- 'var skipErrorProps = object === errorProto || object instanceof Error;\n' +
- '<% } %>' +
-
- // define conditions used in the loop
- '<%' +
- 'var conditions = [];\n' +
- "if (support.enumPrototypes) { conditions.push('!(skipProto && key == \\'prototype\\')'); }\n" +
- "if (support.enumErrorProps) { conditions.push('!(skipErrorProps && (key == \\'message\\' || key == \\'name\\'))'); }" +
- '%>\n' +
-
- // iterate over the object
- 'for (var key in object) {\n<%' +
- " if (useHas) { conditions.push('hasOwnProperty.call(object, key)'); }\n" +
- " if (conditions.length) { %> if (<%= conditions.join(' && ') %>) {\n <% } %>" +
- ' <%= loop %>;' +
- ' <% if (conditions.length) { %>\n }<% } %>\n' +
- '}\n' +
-
- // Lo-Dash skips the `constructor` property when it infers it's iterating
- // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
- // attribute of an existing property and the `constructor` property of a
- // prototype defaults to non-enumerable.
- '<% if (support.nonEnumShadows) { %>\n' +
- 'if (object !== objectProto) {\n' +
- " var ctor = object.constructor,\n" +
- ' isProto = object === (ctor && ctor.prototype),\n' +
- ' className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),\n' +
- ' nonEnum = nonEnumProps[className];\n' +
- ' <% for (var index = 0; index < 7; index++) { %>\n' +
- " key = '<%= shadowedProps[index] %>';\n" +
- ' if ((!(isProto && nonEnum[key]) && hasOwnProperty.call(object, key))<%' +
- ' if (!useHas) { %> || (!nonEnum[key] && object[key] !== objectProto[key])<% }' +
- ' %>) {\n' +
- ' <%= loop %>;\n' +
- ' }' +
- ' <% } %>\n' +
- '}' +
- '<% } %>\n' +
-
- 'return result;'
- );
-
- /*--------------------------------------------------------------------------*/
+ while (++index < length) {
+ result[index] = callback(array[index], index, array);
+ }
+ return result;
+ }
/**
* The base implementation of `_.bind` that creates the bound function and
- * sets its meta data.
+ * sets its metadata.
*
* @private
* @param {Array} data The metadata array.
@@ -1020,8 +1064,8 @@
// avoid `arguments` object use disqualifying optimizations by
// converting it to an array before passing it to `composeArgs`
var index = -1,
- length = arguments.length,
- args = Array(length);
+ length = arguments.length,
+ args = Array(length);
while (++index < length) {
args[index] = arguments[index];
@@ -1045,7 +1089,7 @@
/**
* The base implementation of `_.clone` without argument juggling or support
- * for `thisArg` binding.
+ * for `this` binding.
*
* @private
* @param {*} value The value to clone.
@@ -1062,7 +1106,6 @@
return result;
}
}
- // inspect [[Class]]
var isObj = isObject(value);
if (isObj) {
var className = toString.call(value);
@@ -1090,7 +1133,6 @@
var isArr = isArray(value);
if (isDeep) {
// check for circular references and return corresponding clone
- var initedStack = !stackA;
stackA || (stackA = []);
stackB || (stackB = []);
@@ -1124,8 +1166,8 @@
stackB.push(result);
// recursively populate clone (susceptible to call stack limits)
- (isArr ? baseEach : baseForOwn)(value, function(objValue, key) {
- result[key] = baseClone(objValue, isDeep, callback, stackA, stackB);
+ (isArr ? arrayEach : baseForOwn)(value, function(valValue, key) {
+ result[key] = baseClone(valValue, isDeep, callback, stackA, stackB);
});
return result;
@@ -1165,7 +1207,7 @@
* @param {*} [func=identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {number} [argCount] The number of arguments the callback accepts.
- * @returns {Function} Returns a callback function.
+ * @returns {Function} Returns the new function.
*/
function baseCreateCallback(func, thisArg, argCount) {
if (typeof func != 'function') {
@@ -1201,8 +1243,8 @@
case 1: return function(value) {
return func.call(thisArg, value);
};
- case 2: return function(a, b) {
- return func.call(thisArg, a, b);
+ case 2: return function(value, other) {
+ return func.call(thisArg, value, other);
};
case 3: return function(value, index, collection) {
return func.call(thisArg, value, index, collection);
@@ -1216,7 +1258,7 @@
/**
* The base implementation of `createWrapper` that creates the wrapper and
- * sets its meta data.
+ * sets its metadata.
*
* @private
* @param {Array} data The metadata array.
@@ -1252,14 +1294,19 @@
if (partialRightArgs) {
args = composeArgsRight(partialRightArgs, partialRightHolders, args);
}
- if (isCurry && length < arity) {
- bitmask |= PARTIAL_FLAG;
- bitmask &= ~PARTIAL_RIGHT_FLAG
- if (!isCurryBound) {
- bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+ if (isCurry) {
+ var newPartialHolders = getHolders(args);
+ length -= newPartialHolders.length;
+
+ if (length < arity) {
+ bitmask |= PARTIAL_FLAG;
+ bitmask &= ~PARTIAL_RIGHT_FLAG
+ if (!isCurryBound) {
+ bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG);
+ }
+ var newArity = nativeMax(arity - length, 0);
+ return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, newPartialHolders]);
}
- var newArity = nativeMax(0, arity - length);
- return baseCreateWrapper([func, bitmask, newArity, thisArg, args, null, []]);
}
var thisBinding = isBind ? thisArg : this;
if (isBindKey) {
@@ -1283,7 +1330,7 @@
* @private
* @param {Array} array The array to process.
* @param {Array} [values] The array of values to exclude.
- * @returns {Array} Returns a new array of filtered values.
+ * @returns {Array} Returns the new array of filtered values.
*/
function baseDifference(array, values) {
var length = array ? array.length : 0;
@@ -1324,7 +1371,7 @@
/**
* The base implementation of `_.forEach` without support for callback
- * shorthands or `thisArg` binding.
+ * shorthands or `this` binding.
*
* @private
* @param {Array|Object|string} collection The collection to iterate over.
@@ -1336,7 +1383,7 @@
iterable = collection,
length = collection ? collection.length : 0;
- if (typeof length == 'number') {
+ if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
if (support.unindexedChars && isString(iterable)) {
iterable = iterable.split('');
}
@@ -1353,7 +1400,7 @@
/**
* The base implementation of `_.forEachRight` without support for callback
- * shorthands or `thisArg` binding.
+ * shorthands or `this` binding.
*
* @private
* @param {Array|Object|string} collection The collection to iterate over.
@@ -1364,7 +1411,7 @@
var iterable = collection,
length = collection ? collection.length : 0;
- if (typeof length == 'number') {
+ if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
if (support.unindexedChars && isString(iterable)) {
iterable = iterable.split('');
}
@@ -1379,16 +1426,41 @@
return collection;
}
+ /**
+ * The base implementation of `_.find`, `_.findLast`, `_.findKey`, and `_.findLastKey`
+ * without support for callback shorthands or `this` binding which iterates
+ * over `collection` using the provided `eachFunc`.
+ *
+ * @private
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function} predicate The function called per iteration.
+ * @param {Function} eachFunc The function to iterate over the collection.
+ * @param {boolean} [retKey=false] A flag to indicate returning the key of
+ * the found element instead of the element itself.
+ * @returns {*} Returns the found element or its key, else `undefined`.
+ */
+ function baseFind(collection, predicate, eachFunc, retKey) {
+ var result;
+
+ eachFunc(collection, function(value, key, collection) {
+ if (predicate(value, key, collection)) {
+ result = retKey ? key : value;
+ return false;
+ }
+ });
+ return result;
+ }
+
/**
* The base implementation of `_.flatten` without support for callback
- * shorthands or `thisArg` binding.
+ * shorthands or `this` binding.
*
* @private
* @param {Array} array The array to flatten.
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
* @param {boolean} [isStrict=false] A flag to restrict flattening to arrays and `arguments` objects.
* @param {number} [fromIndex=0] The index to start from.
- * @returns {Array} Returns a new flattened array.
+ * @returns {Array} Returns the new flattened array.
*/
function baseFlatten(array, isShallow, isStrict, fromIndex) {
var index = (fromIndex || 0) - 1,
@@ -1420,17 +1492,20 @@
}
/**
- * The base implementation of `_.forOwn` without support for callback
- * shorthands or `thisArg` binding.
+ * The base implementation of `baseForIn` and `baseForOwn` which iterates
+ * over `object` properties returned by `keysFunc` executing the callback
+ * for each property. Callbacks may exit iteration early by explicitly
+ * returning `false`.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
- function baseForOwn(object, callback) {
+ function baseFor(object, callback, keysFunc) {
var index = -1,
- props = keys(object),
+ props = keysFunc(object),
length = props.length;
while (++index < length) {
@@ -1443,16 +1518,17 @@
}
/**
- * The base implementation of `_.forOwnRight` without support for callback
- * shorthands or `thisArg` binding.
+ * This function is like `baseFor` except that it iterates over properties
+ * in the opposite order.
*
* @private
* @param {Object} object The object to iterate over.
* @param {Function} callback The function called per iteration.
+ * @param {Function} keysFunc The function to get the keys of `object`.
* @returns {Object} Returns `object`.
*/
- function baseForOwnRight(object, callback) {
- var props = keys(object),
+ function baseForRight(object, callback, keysFunc) {
+ var props = keysFunc(object),
length = props.length;
while (length--) {
@@ -1465,137 +1541,190 @@
}
/**
- * The base implementation of `_.isEqual`, without support for `thisArg` binding,
- * that allows partial "_.where" style comparisons.
+ * The base implementation of `_.forIn` without support for callback
+ * shorthands or `this` binding.
*
* @private
- * @param {*} a The value to compare.
- * @param {*} b The other value to compare.
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForIn(object, callback) {
+ return baseFor(object, callback, keysIn);
+ }
+
+ /**
+ * The base implementation of `_.forOwn` without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwn(object, callback) {
+ return baseFor(object, callback, keys);
+ }
+
+ /**
+ * The base implementation of `_.forOwnRight` without support for callback
+ * shorthands or `this` binding.
+ *
+ * @private
+ * @param {Object} object The object to iterate over.
+ * @param {Function} callback The function called per iteration.
+ * @returns {Object} Returns `object`.
+ */
+ function baseForOwnRight(object, callback) {
+ return baseForRight(object, callback, keys);
+ }
+
+ /**
+ * The base implementation of `_.isEqual`, without support for `thisArg`
+ * binding, that allows partial "_.where" style comparisons.
+ *
+ * @private
+ * @param {*} value The value to compare to `other`.
+ * @param {*} other The value to compare to `value`.
* @param {Function} [callback] The function to customize comparing values.
* @param {Function} [isWhere=false] A flag to indicate performing partial comparisons.
- * @param {Array} [stackA=[]] Tracks traversed `a` objects.
- * @param {Array} [stackB=[]] Tracks traversed `b` objects.
+ * @param {Array} [stackA=[]] Tracks traversed `value` objects.
+ * @param {Array} [stackB=[]] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
*/
- function baseIsEqual(a, b, callback, isWhere, stackA, stackB) {
+ function baseIsEqual(value, other, callback, isWhere, stackA, stackB) {
if (callback) {
- var result = callback(a, b);
+ var result = callback(value, other);
if (typeof result != 'undefined') {
return !!result;
}
}
// exit early for identical values
- if (a === b) {
+ if (value === other) {
// treat `+0` vs. `-0` as not equal
- return a !== 0 || (1 / a == 1 / b);
+ return value !== 0 || (1 / value == 1 / other);
}
- var type = typeof a,
- otherType = typeof b;
+ var valType = typeof value,
+ othType = typeof other;
// exit early for unlike primitive values
- if (a === a && (a == null || b == null ||
- (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) {
+ if (value === value && (value == null || other == null ||
+ (valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) {
return false;
}
- // compare [[Class]] names
- var className = toString.call(a),
- otherClass = toString.call(b);
+ var valClass = toString.call(value),
+ othClass = toString.call(other),
+ valIsArg = valClass == argsClass,
+ othIsArg = othClass == argsClass;
- if (className == argsClass) {
- className = objectClass;
+ if (valIsArg) {
+ valClass = objectClass;
}
- if (otherClass == argsClass) {
- otherClass = objectClass;
+ if (othIsArg) {
+ othClass = objectClass;
}
- if (className != otherClass) {
+ if (valClass != othClass) {
return false;
}
- switch (className) {
+ switch (valClass) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal
- return +a == +b;
+ return +value == +other;
case numberClass:
// treat `NaN` vs. `NaN` as equal
- return (a != +a)
- ? b != +b
+ return (value != +value)
+ ? other != +other
// but treat `-0` vs. `+0` as not equal
- : (a == 0 ? (1 / a == 1 / b) : a == +b);
+ : (value == 0 ? (1 / value == 1 / other) : value == +other);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4)
// treat string primitives and their corresponding object instances as equal
- return a == String(b);
+ return value == String(other);
}
- var isArr = className == arrayClass;
+ var isArr = valClass == arrayClass;
if (!isArr) {
- // unwrap any `lodash` wrapped values
- var aWrapped = hasOwnProperty.call(a, '__wrapped__'),
- bWrapped = hasOwnProperty.call(b, '__wrapped__');
-
- if (aWrapped || bWrapped) {
- return baseIsEqual(aWrapped ? a.__wrapped__ : a, bWrapped ? b.__wrapped__ : b, callback, isWhere, stackA, stackB);
- }
// exit for functions and DOM nodes
- if (className != objectClass || (!support.nodeClass && (isNode(a) || isNode(b)))) {
+ if (valClass != objectClass || (!support.nodeClass && (isNode(value) || isNode(other)))) {
return false;
}
- // in older versions of Opera, `arguments` objects have `Array` constructors
- var ctorA = !support.argsObject && isArguments(a) ? Object : a.constructor,
- ctorB = !support.argsObject && isArguments(b) ? Object : b.constructor;
+ // unwrap any `lodash` wrapped values
+ var valWrapped = hasOwnProperty.call(value, '__wrapped__'),
+ othWrapped = hasOwnProperty.call(other, '__wrapped__');
- // non `Object` object instances with different constructors are not equal
- if (ctorA != ctorB &&
- !(hasOwnProperty.call(a, 'constructor') && hasOwnProperty.call(b, 'constructor')) &&
- !(isFunction(ctorA) && ctorA instanceof ctorA && isFunction(ctorB) && ctorB instanceof ctorB) &&
- ('constructor' in a && 'constructor' in b)
- ) {
+ if (valWrapped || othWrapped) {
+ return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, callback, isWhere, stackA, stackB);
+ }
+ if (!support.argsObject) {
+ valIsArg = isArguments(value);
+ othIsArg = isArguments(other);
+ }
+ var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'),
+ hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor');
+
+ if (hasValCtor != hasOthCtor) {
return false;
}
+ if (!hasValCtor) {
+ // in older versions of Opera, `arguments` objects have `Array` constructors
+ var valCtor = valIsArg ? Object : value.constructor,
+ othCtor = othIsArg ? Object : other.constructor;
+
+ // non `Object` object instances with different constructors are not equal
+ if (valCtor != othCtor &&
+ !(isFunction(valCtor) && valCtor instanceof valCtor && isFunction(othCtor) && othCtor instanceof othCtor) &&
+ ('constructor' in value && 'constructor' in other)
+ ) {
+ return false;
+ }
+ }
}
// assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1
// section 15.12.3, abstract operation `JO` (http://es5.github.io/#x15.12.3)
- var initedStack = !stackA;
stackA || (stackA = []);
stackB || (stackB = []);
var length = stackA.length;
while (length--) {
- if (stackA[length] == a) {
- return stackB[length] == b;
+ if (stackA[length] == value) {
+ return stackB[length] == other;
}
}
var size = 0;
result = true;
- // add `a` and `b` to the stack of traversed objects
- stackA.push(a);
- stackB.push(b);
+ // add `value` and `other` to the stack of traversed objects
+ stackA.push(value);
+ stackB.push(other);
// recursively compare objects and arrays (susceptible to call stack limits)
if (isArr) {
// compare lengths to determine if a deep comparison is necessary
- length = a.length;
- size = b.length;
+ length = value.length;
+ size = other.length;
result = size == length;
if (result || isWhere) {
// deep compare the contents, ignoring non-numeric properties
while (size--) {
var index = length,
- value = b[size];
+ othValue = other[size];
if (isWhere) {
while (index--) {
- if ((result = baseIsEqual(a[index], value, callback, isWhere, stackA, stackB))) {
+ if ((result = baseIsEqual(value[index], othValue, callback, isWhere, stackA, stackB))) {
break;
}
}
- } else if (!(result = baseIsEqual(a[size], value, callback, isWhere, stackA, stackB))) {
+ if (!result) {
+ break;
+ }
+ } else if (!(result = baseIsEqual(value[size], othValue, callback, isWhere, stackA, stackB))) {
break;
}
}
@@ -1604,20 +1733,20 @@
else {
// deep compare objects using `forIn`, instead of `forOwn`, to avoid `Object.keys`
// which, in this case, is more costly
- baseForIn(b, function(value, key, b) {
- if (hasOwnProperty.call(b, key)) {
+ baseForIn(other, function(othValue, key, other) {
+ if (hasOwnProperty.call(other, key)) {
// count the number of properties.
size++;
// deep compare each property value.
- return (result = hasOwnProperty.call(a, key) && baseIsEqual(a[key], value, callback, isWhere, stackA, stackB));
+ return (result = hasOwnProperty.call(value, key) && baseIsEqual(value[key], othValue, callback, isWhere, stackA, stackB));
}
});
if (result && !isWhere) {
// ensure both objects have the same number of properties
- baseForIn(a, function(value, key, a) {
- if (hasOwnProperty.call(a, key)) {
- // `size` will be `-1` if `a` has more properties than `b`
+ baseForIn(value, function(valValue, key, value) {
+ if (hasOwnProperty.call(value, key)) {
+ // `size` will be `-1` if `value` has more properties than `other`
return (result = --size > -1);
}
});
@@ -1631,7 +1760,7 @@
/**
* The base implementation of `_.merge` without argument juggling or support
- * for `thisArg` binding.
+ * for `this` binding.
*
* @private
* @param {Object} object The destination object.
@@ -1641,7 +1770,7 @@
* @param {Array} [stackB=[]] Associates values with source counterparts.
*/
function baseMerge(object, source, callback, stackA, stackB) {
- (isArray(source) ? baseEach : baseForOwn)(source, function(source, key) {
+ (isArray(source) ? arrayEach : baseForOwn)(source, function(source, key) {
var found,
isArr,
result = source,
@@ -1701,7 +1830,7 @@
* @private
* @param {number} min The minimum possible value.
* @param {number} max The maximum possible value.
- * @returns {number} Returns a random number.
+ * @returns {number} Returns the random number.
*/
function baseRandom(min, max) {
return min + floor(nativeRandom() * (max - min + 1));
@@ -1709,13 +1838,13 @@
/**
* The base implementation of `_.uniq` without support for callback shorthands
- * or `thisArg` binding.
+ * or `this` binding.
*
* @private
* @param {Array} array The array to process.
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
* @param {Function} [callback] The function called per iteration.
- * @returns {Array} Returns a duplicate-value-free array.
+ * @returns {Array} Returns the new duplicate-value-free array.
*/
function baseUniq(array, isSorted, callback) {
var length = array ? array.length : 0;
@@ -1768,15 +1897,37 @@
return result;
}
+ /**
+ * The base implementation of `_.values` and `_.valuesIn` which creates an
+ * array of `object` property values corresponding to the property names
+ * returned by `keysFunc`.
+ *
+ * @private
+ * @param {Object} object The object to inspect.
+ * @param {Function} keysFunc The function to get the keys of `object`.
+ * @returns {Object} Returns the array of property values.
+ */
+ function baseValues(object, keysFunc) {
+ var index = -1,
+ props = keysFunc(object),
+ length = props.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = object[props[index]];
+ }
+ return result;
+ }
+
/**
* Creates an array that is the composition of partially applied arguments,
* placeholders, and provided arguments into a single array of arguments.
*
* @private
- * @param {Array} partialArg An array of arguments to prepend to those provided.
+ * @param {Array} partialArgs An array of arguments to prepend to those provided.
* @param {Array} partialHolders An array of `partialArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
- * @returns {Array} Returns a new array of composed arguments.
+ * @returns {Array} Returns the new array of composed arguments.
*/
function composeArgs(partialArgs, partialHolders, args) {
var holdersLength = partialHolders.length,
@@ -1803,10 +1954,10 @@
* is tailored for `_.partialRight`.
*
* @private
- * @param {Array} partialRightArg An array of arguments to append to those provided.
+ * @param {Array} partialRightArgs An array of arguments to append to those provided.
* @param {Array} partialHolders An array of `partialRightArgs` placeholder indexes.
* @param {Array|Object} args The provided arguments.
- * @returns {Array} Returns a new array of composed arguments.
+ * @returns {Array} Returns the new array of composed arguments.
*/
function composeArgsRight(partialRightArgs, partialRightHolders, args) {
var holdersIndex = -1,
@@ -1831,22 +1982,22 @@
}
/**
- * Creates a function that aggregates a collection, creating an object or
- * array composed from the results of running each element of the collection
- * through a callback. The given `setter` function sets the keys and values
- * of the composed object or array.
+ * Creates a function that aggregates a collection, creating an accumulator
+ * object composed from the results of running each element in the collection
+ * through a callback. The given setter function sets the keys and values of
+ * the accumulator object. If `initializer` is provided will be used to
+ * initialize the accumulator object.
*
* @private
- * @param {Function} setter The setter function.
- * @param {boolean} [retArray=false] A flag to indicate that the aggregator
- * function should return an array.
+ * @param {Function} setter The function to set keys and values of the accumulator object.
+ * @param {Function} [initializer] The function to initialize the accumulator object.
* @returns {Function} Returns the new aggregator function.
*/
- function createAggregator(setter, retArray) {
+ function createAggregator(setter, initializer) {
return function(collection, callback, thisArg) {
- var result = retArray ? [[], []] : {};
-
+ var result = initializer ? initializer() : {};
callback = lodash.createCallback(callback, thisArg, 3);
+
if (isArray(collection)) {
var index = -1,
length = collection.length;
@@ -1869,7 +2020,7 @@
*
* @private
* @param {Array} [array=[]] The array to search.
- * @returns {Object} Returns the cache object.
+ * @returns {Object} Returns the new cache object.
*/
var createCache = Set && function(array) {
var cache = new Set,
@@ -1883,8 +2034,31 @@
};
/**
- * Creates a function that, when called, either curries or invokes `func`
- * with an optional `this` binding and partially applied arguments.
+ * Creates the pad required for `string` based on the given padding length.
+ * The `chars` string may be truncated if the number of padding characters
+ * exceeds the padding length.
+ *
+ * @private
+ * @param {string} string The string to create padding for.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the pad for `string`.
+ */
+ function createPad(string, length, chars) {
+ var strLength = string.length;
+ length = +length;
+
+ if (strLength >= length || !nativeIsFinite(length)) {
+ return '';
+ }
+ var padLength = length - strLength;
+ chars = chars == null ? ' ' : String(chars);
+ return repeat(chars, ceil(padLength / chars.length)).slice(0, padLength);
+ }
+
+ /**
+ * Creates a function that either curries or invokes `func` with an optional
+ * `this` binding and partially applied arguments.
*
* @private
* @param {Function|string} func The function or method name to reference.
@@ -1902,18 +2076,16 @@
* provided to the new function.
* @param {Array} [partialRightArgs] An array of arguments to append to those
* provided to the new function.
- * @param {Array} [partialHolders] An array of `partialArgs` placeholder indexes.
- * @param {Array} [partialRightHolders] An array of `partialRightArgs` placeholder indexes.
* @returns {Function} Returns the new function.
*/
- function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders) {
+ function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs) {
var isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG,
isPartial = bitmask & PARTIAL_FLAG,
isPartialRight = bitmask & PARTIAL_RIGHT_FLAG;
if (!isBindKey && !isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
if (isPartial && !partialArgs.length) {
bitmask &= ~PARTIAL_FLAG;
@@ -1969,17 +2141,17 @@
data[1] |= bitmask;
return createWrapper.apply(null, data);
}
- if (arity == null) {
- arity = isBindKey ? 0 : func.length;
- } else if (arity < 0) {
- arity = 0;
- }
if (isPartial) {
- partialHolders = getHolders(partialArgs);
+ var partialHolders = getHolders(partialArgs);
}
if (isPartialRight) {
- partialRightHolders = getHolders(partialRightArgs);
+ var partialRightHolders = getHolders(partialRightArgs);
}
+ if (arity == null) {
+ arity = isBindKey ? 0 : func.length;
+ }
+ arity = nativeMax(arity, 0);
+
// fast path for `_.bind`
data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs, partialHolders, partialRightHolders];
return (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG))
@@ -1988,40 +2160,11 @@
}
/**
- * Creates compiled iteration functions.
- *
- * @private
- * @param {Object} [options] The compile options object.
- * @param {string} [options.args] A comma separated string of iteration function arguments.
- * @param {string} [options.init] The string representation of the initial `result` value.
- * @param {string} [options.loop] Code to execute in the object loop.
- * @param {boolean} [options.useHas] Specify using `hasOwnProperty` checks in the object loop.
- * @returns {Function} Returns the compiled function.
- */
- function createIterator(options) {
- options.shadowedProps = shadowedProps;
- options.support = support;
-
- // create the function factory
- var factory = Function(
- 'errorClass, errorProto, hasOwnProperty, isArguments, isObject, objectProto, ' +
- 'nonEnumProps, stringClass, stringProto, toString',
- 'return function(' + options.args + ') {\n' + iteratorTemplate(options) + '\n}'
- );
-
- // return the compiled function
- return factory(
- errorClass, errorProto, hasOwnProperty, isArguments, isObject, objectProto,
- nonEnumProps, stringClass, stringProto, toString
- );
- }
-
- /**
- * Finds the indexes of all placeholder elements in a given array.
+ * Finds the indexes of all placeholder elements in `array`.
*
* @private
* @param {Array} array The array to inspect.
- * @returns {Array} Returns a new array of placeholder indexes.
+ * @returns {Array} Returns the new array of placeholder indexes.
*/
function getHolders(array) {
var index = -1,
@@ -2038,15 +2181,15 @@
/**
* Gets the appropriate "indexOf" function. If the `_.indexOf` method is
- * customized this method returns the custom method, otherwise it returns
+ * customized this function returns the custom method, otherwise it returns
* the `baseIndexOf` function.
*
* @private
* @returns {Function} Returns the "indexOf" function.
*/
function getIndexOf() {
- var result = (result = lodash.indexOf) === indexOf ? baseIndexOf : result;
- return result;
+ var result = lodash.indexOf || indexOf;
+ return result === indexOf ? baseIndexOf : result;
}
/**
@@ -2054,7 +2197,7 @@
*
* @private
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a native function, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a native function, else `false`.
*/
function isNative(value) {
return typeof value == 'function' && reNative.test(fnToString.call(value));
@@ -2073,10 +2216,9 @@
};
/**
- * A fallback implementation of `isPlainObject` which checks if a given value
- * is an object created by the `Object` constructor, assuming objects created
- * by the `Object` constructor have no inherited enumerable properties and that
- * there are no `Object.prototype` extensions.
+ * A fallback implementation of `_.isPlainObject` which checks if `value`
+ * is an object created by the `Object` constructor or has a `[[Prototype]]`
+ * of `null`.
*
* @private
* @param {*} value The value to check.
@@ -2086,7 +2228,7 @@
var ctor,
result;
- // avoid non Object objects, `arguments` objects, and DOM elements
+ // avoid non `Object` objects, `arguments` objects, and DOM elements
if (!(value && toString.call(value) == objectClass) ||
(!hasOwnProperty.call(value, 'constructor') &&
(ctor = value.constructor, isFunction(ctor) && !(ctor instanceof ctor))) ||
@@ -2113,67 +2255,36 @@
return typeof result == 'undefined' || hasOwnProperty.call(value, result);
}
- /*--------------------------------------------------------------------------*/
-
/**
- * Checks if `value` is an `arguments` object.
- *
- * @static
- * @memberOf _
- * @category Objects
- * @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is an `arguments` object, else `false`.
- * @example
- *
- * (function() { return _.isArguments(arguments); })(1, 2, 3);
- * // => true
- *
- * _.isArguments([1, 2, 3]);
- * // => false
- */
- function isArguments(value) {
- return value && typeof value == 'object' && typeof value.length == 'number' &&
- toString.call(value) == argsClass || false;
- }
- // fallback for environments that can't detect `arguments` objects by [[Class]]
- if (!support.argsClass) {
- isArguments = function(value) {
- return value && typeof value == 'object' && typeof value.length == 'number' &&
- hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee') || false;
- };
- }
-
- /**
- * The base implementation of `_.forIn` without support for callback
- * shorthands or `thisArg` binding.
+ * A fallback implementation of `Object.keys` which creates an array of the
+ * own enumerable property names of `object`.
*
* @private
- * @param {Object} object The object to iterate over.
- * @param {Function} callback The function called per iteration.
- * @returns {Object} Returns `object`.
- */
- var baseForIn = createIterator({
- 'args': 'object, callback',
- 'init': 'object',
- 'loop': 'if (callback(object[key], key, object) === false) {\n return result;\n }',
- 'useHas': false
- });
-
- /**
- * A fallback implementation of `Object.keys` which produces an array of the
- * given object's own enumerable property names.
- *
- * @private
- * @type Function
* @param {Object} object The object to inspect.
- * @returns {Array} Returns an array of property names.
+ * @returns {Array} Returns the array of property names.
*/
- var shimKeys = createIterator({
- 'args': 'object',
- 'init': '[]',
- 'loop': 'result.push(key)',
- 'useHas': true
- });
+ function shimKeys(object) {
+ var keyIndex,
+ index = -1,
+ props = keysIn(object),
+ length = props.length,
+ objLength = length && object.length,
+ maxIndex = objLength - 1,
+ result = [];
+
+ var allowIndexes = typeof objLength == 'number' && objLength > 0 &&
+ (isArray(object) || (support.nonEnumArgs && isArguments(object)) ||
+ (support.nonEnumStrings && isString(object)));
+
+ while (++index < length) {
+ var key = props[index];
+ if ((allowIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0)) ||
+ hasOwnProperty.call(object, key)) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
/*--------------------------------------------------------------------------*/
@@ -2185,7 +2296,7 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to compact.
- * @returns {Array} Returns a new array of filtered values.
+ * @returns {Array} Returns the new array of filtered values.
* @example
*
* _.compact([0, 1, false, 2, '', 3]);
@@ -2215,24 +2326,171 @@
* @category Arrays
* @param {Array} array The array to process.
* @param {...Array} [values] The arrays of values to exclude.
- * @returns {Array} Returns a new array of filtered values.
+ * @returns {Array} Returns the new array of filtered values.
* @example
*
* _.difference([1, 2, 3], [5, 2, 10]);
* // => [1, 3]
*/
- function difference(array) {
- return baseDifference(array, baseFlatten(arguments, true, true, 1));
+ function difference() {
+ var index = -1,
+ length = arguments.length;
+
+ while (++index < length) {
+ var value = arguments[index];
+ if (isArray(value) || isArguments(value)) {
+ break;
+ }
+ }
+ return baseDifference(arguments[index], baseFlatten(arguments, true, true, ++index));
}
/**
- * This method is like `_.find` except that it returns the index of the first
- * element that passes the callback check, instead of the element itself.
+ * Creates a slice of `array` with `n` elements dropped from the beginning.
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.drop([1, 2, 3], 1);
+ * // => [2, 3]
+ *
+ * _.drop([1, 2, 3], 2);
+ * // => [3]
+ *
+ * _.drop([1, 2, 3], 5);
+ * // => []
+ *
+ * _.drop([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */
+ var drop = rest;
+
+ /**
+ * Creates a slice of `array` with `n` elements dropped from the end.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to drop.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRight([1, 2, 3], 1);
+ * // => [1, 2]
+ *
+ * _.dropRight([1, 2, 3], 2);
+ * // => [1]
+ *
+ * _.dropRight([1, 2, 3], 5);
+ * // => []
+ *
+ * _.dropRight([1, 2, 3], 0);
+ * // => [1, 2, 3]
+ */
+ var dropRight = initial;
+
+ /**
+ * Creates a slice of `array` excluding elements dropped from the end.
+ * Elements will be dropped until the predicate returns falsey. The predicate
+ * is bound to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per element.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropRightWhile([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [1]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'employer': 'slate' },
+ * { 'name': 'fred', 'employer': 'slate', 'blocked': true },
+ * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.dropRightWhile(characters, 'blocked'), 'name');
+ * // => ['barney']
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.dropRightWhile(characters, { 'employer': 'na' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ var dropRightWhile = initial;
+
+ /**
+ * Creates a slice of `array` excluding elements dropped from the beginning.
+ * Elements will be dropped until the predicate returns falsey. The predicate
+ * is bound to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per element.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.dropWhile([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'employer': 'slate', 'blocked': true },
+ * { 'name': 'fred', 'employer': 'slate' },
+ * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.dropWhile(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.dropWhile(characters, { 'employer': 'slate' }), 'name');
+ * // => ['pebbles']
+ */
+ var dropWhile = rest;
+
+ /**
+ * This method is like `_.find` except that it returns the index of the first
+ * element the predicate returns truthy for, instead of the element itself.
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -2240,10 +2498,10 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
@@ -2266,13 +2524,13 @@
* _.findIndex(characters, 'blocked');
* // => 1
*/
- function findIndex(array, callback, thisArg) {
+ function findIndex(array, predicate, thisArg) {
var index = -1,
length = array ? array.length : 0;
- callback = lodash.createCallback(callback, thisArg, 3);
+ predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length) {
- if (callback(array[index], index, array)) {
+ if (predicate(array[index], index, array)) {
return index;
}
}
@@ -2281,12 +2539,12 @@
/**
* This method is like `_.findIndex` except that it iterates over elements
- * of a `collection` from right to left.
+ * of a collection from right to left.
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -2294,10 +2552,10 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to search.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
* @returns {number} Returns the index of the found element, else `-1`.
* @example
*
@@ -2320,12 +2578,12 @@
* _.findLastIndex(characters, 'blocked');
* // => 2
*/
- function findLastIndex(array, callback, thisArg) {
+ function findLastIndex(array, predicate, thisArg) {
var length = array ? array.length : 0;
- callback = lodash.createCallback(callback, thisArg, 3);
+ predicate = lodash.createCallback(predicate, thisArg, 3);
while (length--) {
- if (callback(array[length], length, array)) {
+ if (predicate(array[length], length, array)) {
return length;
}
}
@@ -2333,80 +2591,47 @@
}
/**
- * Gets the first element or first `n` elements of an array. If a callback
- * is provided elements at the beginning of the array are returned as long
- * as the callback returns truey. The callback is bound to `thisArg` and
- * invoked with three arguments; (value, index, array).
+ * Gets the first element of `array`.
*
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
+ * Note: The `n` and `predicate` arguments are deprecated; replace with
+ * `_.take` and `_.takeWhile` respectively.
*
* @static
* @memberOf _
- * @alias head, take
+ * @alias head
* @category Arrays
* @param {Array} array The array to query.
- * @param {Function|Object|number|string} [callback] The function called
- * per element or the number of elements to return. If a property name or
- * object is provided it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {*} Returns the first element(s) of `array`.
+ * @returns {*} Returns the first element of `array`.
* @example
*
* _.first([1, 2, 3]);
* // => 1
*
- * // returns the first two elements
- * _.first([1, 2, 3], 2);
- * // => [1, 2]
- *
- * // returns elements from the beginning until the callback result is falsey
- * _.first([1, 2, 3], function(num) {
- * return num < 3;
- * });
- * // => [1, 2]
- *
- * var characters = [
- * { 'name': 'barney', 'employer': 'slate', 'blocked': true },
- * { 'name': 'fred', 'employer': 'slate' },
- * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.first(characters, 'blocked');
- * // => [{ 'name': 'barney', 'employer': 'slate', 'blocked': true }]
- *
- * // using "_.where" callback shorthand
- * _.pluck(_.first(characters, { 'employer': 'slate' }), 'name');
- * // => ['barney', 'fred']
+ * _.first([]);
+ * // => undefined
*/
- function first(array, callback, thisArg) {
- if (typeof callback != 'number' && callback != null) {
+ function first(array, predicate, thisArg) {
+ if (typeof predicate != 'number' && predicate != null) {
var index = -1,
length = array ? array.length : 0,
n = 0;
- callback = lodash.createCallback(callback, thisArg, 3);
- while (++index < length && callback(array[index], index, array)) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ while (++index < length && predicate(array[index], index, array)) {
n++;
}
} else {
- n = callback;
+ n = predicate;
if (n == null || thisArg) {
return array ? array[0] : undefined;
}
}
- return slice(array, 0, n > 0 ? n : 0);
+ return slice(array, 0, n < 0 ? 0 : n);
}
/**
* Flattens a nested array (the nesting can be to any depth). If `isShallow`
- * is truey, the array will only be flattened a single level. If a callback
+ * is truthy, the array will only be flattened a single level. If a callback
* is provided each element of the array is passed through the callback before
* flattening. The callback is bound to `thisArg` and invoked with three
* arguments; (value, index, array).
@@ -2423,11 +2648,11 @@
* @category Arrays
* @param {Array} array The array to flatten.
* @param {boolean} [isShallow=false] A flag to restrict flattening to a single level.
- * @param {Function|Object|string} [callback=identity] The function called
- * per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Function|Object|string} [callback] The function called per iteration.
+ * If a property name or object is provided it will be used to create a "_.pluck"
+ * or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new flattened array.
+ * @returns {Array} Returns the new flattened array.
* @example
*
* _.flatten([1, [2], [3, [[4]]]]);
@@ -2481,7 +2706,7 @@
* @param {*} value The value to search for.
* @param {boolean|number} [fromIndex=0] The index to search from or `true`
* to perform a binary search on a sorted array.
- * @returns {number} Returns the index of the matched value or `-1`.
+ * @returns {number} Returns the index of the matched value, else `-1`.
* @example
*
* _.indexOf([1, 2, 3, 1, 2, 3], 2);
@@ -2498,7 +2723,7 @@
function indexOf(array, value, fromIndex) {
var length = array ? array.length : 0;
if (typeof fromIndex == 'number') {
- fromIndex = fromIndex < 0 ? nativeMax(0, length + fromIndex) : (fromIndex || 0);
+ fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
} else if (fromIndex) {
var index = sortedIndex(array, value);
return (length && array[index] === value) ? index : -1;
@@ -2507,73 +2732,37 @@
}
/**
- * Gets all but the last element or last `n` elements of an array. If a
- * callback is provided elements at the end of the array are excluded from
- * the result as long as the callback returns truey. The callback is bound
- * to `thisArg` and invoked with three arguments; (value, index, array).
+ * Gets all but the last element of `array`.
*
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
+ * Note: The `n` and `predicate` arguments are deprecated; replace with
+ * `_.dropRight` and `_.dropRightWhile` respectively.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to query.
- * @param {Function|Object|number|string} [callback=1] The function called
- * per element or the number of elements to exclude. If a property name or
- * object is provided it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a slice of `array`.
+ * @returns {Array} Returns the slice of `array`.
* @example
*
* _.initial([1, 2, 3]);
* // => [1, 2]
- *
- * // excludes the last two elements
- * _.initial([1, 2, 3], 2);
- * // => [1]
- *
- * // excludes elements from the end until the callback fails
- * _.initial([1, 2, 3], function(num) {
- * return num > 1;
- * });
- * // => [1]
- *
- * var characters = [
- * { 'name': 'barney', 'employer': 'slate' },
- * { 'name': 'fred', 'employer': 'slate', 'blocked': true },
- * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.initial(characters, 'blocked');
- * // => [{ 'name': 'barney', 'blocked': false, 'employer': 'slate' }]
- *
- * // using "_.where" callback shorthand
- * _.pluck(_.initial(characters, { 'employer': 'na' }), 'name');
- * // => ['barney', 'fred']
*/
- function initial(array, callback, thisArg) {
+ function initial(array, predicate, thisArg) {
var length = array ? array.length : 0;
- if (typeof callback != 'number' && callback != null) {
+ if (typeof predicate != 'number' && predicate != null) {
var index = length,
n = 0;
- callback = lodash.createCallback(callback, thisArg, 3);
- while (index-- && callback(array[index], index, array)) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ while (index-- && predicate(array[index], index, array)) {
n++;
}
} else {
- n = (callback == null || thisArg) ? 1 : callback;
+ n = (predicate == null || thisArg) ? 1 : predicate;
}
- n = length - n;
- return slice(array, 0, n > 0 ? n : 0);
+ n = length - (n || 0);
+ return slice(array, 0, n < 0 ? 0 : n);
}
/**
@@ -2583,8 +2772,8 @@
* @static
* @memberOf _
* @category Arrays
- * @param {...Array} [array] The arrays to inspect.
- * @returns {Array} Returns an array of shared values.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of shared values.
* @example
*
* _.intersection([1, 2, 3], [5, 2, 1, 4], [2, 1]);
@@ -2596,36 +2785,37 @@
argsLength = arguments.length,
caches = [],
indexOf = getIndexOf(),
- prereq = createCache && indexOf === baseIndexOf,
- seen = [];
+ prereq = createCache && indexOf === baseIndexOf;
while (++argsIndex < argsLength) {
var value = arguments[argsIndex];
if (isArray(value) || isArguments(value)) {
args.push(value);
caches.push(prereq && value.length >= 120 &&
- createCache(argsIndex ? args[argsIndex] : seen));
+ createCache(argsIndex && value));
}
}
+ argsLength = args.length;
var array = args[0],
index = -1,
length = array ? array.length : 0,
- result = [];
+ result = [],
+ seen = caches[0];
outer:
while (++index < length) {
- var cache = caches[0];
value = array[index];
-
- if ((cache ? cacheIndexOf(cache, value) : indexOf(seen, value)) < 0) {
+ if ((seen ? cacheIndexOf(seen, value) : indexOf(result, value)) < 0) {
argsIndex = argsLength;
- (cache || seen).push(value);
while (--argsIndex) {
- cache = caches[argsIndex];
+ var cache = caches[argsIndex];
if ((cache ? cacheIndexOf(cache, value) : indexOf(args[argsIndex], value)) < 0) {
continue outer;
}
}
+ if (seen) {
+ seen.push(value);
+ }
result.push(value);
}
}
@@ -2633,89 +2823,46 @@
}
/**
- * Gets the last element or last `n` elements of an array. If a callback is
- * provided elements at the end of the array are returned as long as the
- * callback returns truey. The callback is bound to `thisArg` and invoked
- * with three arguments; (value, index, array).
+ * Gets the last element of `array`.
*
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
+ * Note: The `n` and `predicate` arguments are deprecated; replace with
+ * `_.takeRight` and `_.takeRightWhile` respectively.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to query.
- * @param {Function|Object|number|string} [callback] The function called
- * per element or the number of elements to return. If a property name or
- * object is provided it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {*} Returns the last element(s) of `array`.
+ * @returns {*} Returns the last element of `array`.
* @example
*
* _.last([1, 2, 3]);
* // => 3
- *
- * // returns the last two elements
- * _.last([1, 2, 3], 2);
- * // => [2, 3]
- *
- * // returns elements from the end until the callback fails
- * _.last([1, 2, 3], function(num) {
- * return num > 1;
- * });
- * // => [2, 3]
- *
- * var characters = [
- * { 'name': 'barney', 'employer': 'slate' },
- * { 'name': 'fred', 'employer': 'slate', 'blocked': true },
- * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.pluck(_.last(characters, 'blocked'), 'name');
- * // => ['fred', 'pebbles']
- *
- * // using "_.where" callback shorthand
- * _.last(characters, { 'employer': 'na' });
- * // => [{ 'name': 'pebbles', 'employer': 'na', 'blocked': true }]
*/
- function last(array, callback, thisArg) {
+ function last(array, predicate, thisArg) {
var length = array ? array.length : 0;
- if (typeof callback != 'number' && callback != null) {
+ if (typeof predicate != 'number' && predicate != null) {
var index = length,
n = 0;
- callback = lodash.createCallback(callback, thisArg, 3);
- while (index-- && callback(array[index], index, array)) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ while (index-- && predicate(array[index], index, array)) {
n++;
}
} else {
- n = callback;
+ n = predicate;
if (n == null || thisArg) {
return array ? array[length - 1] : undefined;
}
}
- n = length - n;
- return slice(array, n > 0 ? n : 0);
+ n = length - (n || 0);
+ return slice(array, n < 0 ? 0 : n);
}
/**
- * Gets the index at which the last occurrence of `value` is found using strict
- * equality for comparisons, i.e. `===`. If `fromIndex` is negative, it is used
- * as the offset from the end of the collection.
- *
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
+ * Gets the index at which the last occurrence of `value` is found using
+ * strict equality for comparisons, i.e. `===`. If `fromIndex` is negative,
+ * it is used as the offset from the end of the collection.
*
* @static
* @memberOf _
@@ -2723,7 +2870,7 @@
* @param {Array} array The array to search.
* @param {*} value The value to search for.
* @param {number} [fromIndex=array.length-1] The index to search from.
- * @returns {number} Returns the index of the matched value or `-1`.
+ * @returns {number} Returns the index of the matched value, else `-1`.
* @example
*
* _.lastIndexOf([1, 2, 3, 1, 2, 3], 2);
@@ -2736,7 +2883,7 @@
function lastIndexOf(array, value, fromIndex) {
var index = array ? array.length : 0;
if (typeof fromIndex == 'number') {
- index = (fromIndex < 0 ? nativeMax(0, index + fromIndex) : nativeMin(fromIndex, index - 1)) + 1;
+ index = (fromIndex < 0 ? nativeMax(index + fromIndex, 0) : nativeMin(fromIndex || 0, index - 1)) + 1;
}
while (index--) {
if (array[index] === value) {
@@ -2750,11 +2897,13 @@
* Removes all provided values from `array` using strict equality for
* comparisons, i.e. `===`.
*
+ * Note: Unlike `_.without`, this method mutates `array`.
+ *
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to modify.
- * @param {...*} [value] The values to remove.
+ * @param {...*} [values] The values to remove.
* @returns {Array} Returns `array`.
* @example
*
@@ -2783,79 +2932,74 @@
}
/**
- * Creates an array of numbers (positive and/or negative) progressing from
- * `start` up to but not including `end`. If `start` is less than `stop` a
- * zero-length range is created unless a negative `step` is specified.
+ * Removes elements located at the given indexes and returns an array of
+ * removed elements. Indexes may be specified as an array of indexes or as
+ * individual arguments.
*
- * @static
- * @memberOf _
- * @category Arrays
- * @param {number} [start=0] The start of the range.
- * @param {number} end The end of the range.
- * @param {number} [step=1] The value to increment or decrement by.
- * @returns {Array} Returns a new range array.
- * @example
- *
- * _.range(4);
- * // => [0, 1, 2, 3]
- *
- * _.range(1, 5);
- * // => [1, 2, 3, 4]
- *
- * _.range(0, 20, 5);
- * // => [0, 5, 10, 15]
- *
- * _.range(0, -4, -1);
- * // => [0, -1, -2, -3]
- *
- * _.range(1, 4, 0);
- * // => [1, 1, 1]
- *
- * _.range(0);
- * // => []
- */
- function range(start, end, step) {
- start = +start || 0;
- step = typeof step == 'number' ? step : (+step || 1);
-
- if (end == null) {
- end = start;
- start = 0;
- }
- // use `Array(length)` so engines like Chakra and V8 avoid slower modes
- // http://youtu.be/XAqIpGU8ZZk#t=17m25s
- var index = -1,
- length = nativeMax(0, ceil((end - start) / (step || 1))),
- result = Array(length);
-
- while (++index < length) {
- result[index] = start;
- start += step;
- }
- return result;
- }
-
- /**
- * Removes all elements from an array that the callback returns truey for
- * and returns an array of removed elements. The callback is bound to `thisArg`
- * and invoked with three arguments; (value, index, array).
- *
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
+ * Note: Like `_.pull`, this method mutates `array`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to modify.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {...(number|number[])} [index] The indexes of values to remove,
+ * specified as individual indexes or arrays of indexes.
+ * @returns {Array} Returns the new array of removed elements.
+ * @example
+ *
+ * var array = [5, 10, 15, 20];
+ * var evens = _.removeAt(array, [1, 3]);
+ *
+ * console.log(array);
+ * // => [5, 15]
+ *
+ * console.log(evens);
+ * // => [10, 20]
+ */
+ function pullAt(array) {
+ var previous,
+ index = -1,
+ removals = baseFlatten(arguments, true, false, 1),
+ length = removals.length,
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = array[removals[index]];
+ }
+ removals.sort(baseCompareAscending);
+ while (length--) {
+ var removal = removals[length];
+ if (removal != previous) {
+ splice.call(array, removal, 1);
+ previous = removal;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Removes all elements from `array` that the predicate returns truthy for
+ * and returns an array of removed elements. The predicate is bound to `thisArg`
+ * and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * Note: Unlike `_.filter`, this method mutates `array`.
+ *
+ * @static
+ * @memberOf _
+ * @category Arrays
+ * @param {Array} array The array to modify.
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of removed elements.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the array of removed elements.
* @example
*
* var array = [1, 2, 3, 4, 5, 6];
@@ -2867,15 +3011,15 @@
* console.log(evens);
* // => [2, 4, 6]
*/
- function remove(array, callback, thisArg) {
+ function remove(array, predicate, thisArg) {
var index = -1,
length = array ? array.length : 0,
result = [];
- callback = lodash.createCallback(callback, thisArg, 3);
+ predicate = lodash.createCallback(predicate, thisArg, 3);
while (++index < length) {
var value = array[index];
- if (callback(value, index, array)) {
+ if (predicate(value, index, array)) {
result.push(value);
splice.call(array, index--, 1);
length--;
@@ -2885,132 +3029,36 @@
}
/**
- * Removes elements located at the given indexes and returns an array of
- * removed elements. A hybrid of "_.remove" and "_.at". Indexes may be
- * specified as an array of indexes or as individual arguments.
+ * Gets all but the first element of `array`.
+ *
+ * Note: The `n` and `predicate` arguments are deprecated; replace with
+ * `_.drop` and `_.dropWhile` respectively.
*
* @static
* @memberOf _
- * @category Arrays
- * @param {Array} array The array to modify.
- * @param {...(number|number[])} [index] The indexes of `array` to remove
- * @returns {Array} Returns a new array of removed elements.
- * @example
- *
- * var array = [5, 10, 15, 20, 25, 30];
- * var evens = _.removeAt(array, [1, 3, 5]);
- *
- * console.log(array);
- * // => [5, 15, 25]
- *
- * console.log(evens);
- * // => [10, 20, 30]
- *
- * var greeting = ('good morning').split('');
- * var vowels = _.removeAt(greeting, 1, 2, 6, 9);
- *
- * console.log(greeting.join(''));
- * // => 'gd mrnng'
- *
- * console.log(vowels.join(''));
- * // => 'oooi'
- */
- function removeAt(array, guard) {
- var args = arguments,
- index = -1,
- removals = baseFlatten(args, true, false, 1),
- length = removals.length;
-
- // enables use as a callback for functions like `_.map`
- if (typeof guard == 'number' && args[2] && args[2][guard] === array) {
- length = 1;
- } else {
- removals.sort(baseCompareAscending);
- }
- var result = Array(length),
- adjust = -1,
- removal, previous;
- while(++index < length) {
- removal = removals[index];
- if (removal === previous) {
- result[index] = result[index - 1];
- } else {
- previous = removal;
- result[index] = splice.call(array, removal - ++adjust, 1)[0];
- }
- }
- return result;
- }
-
-
- /**
- * The opposite of `_.initial`; this method gets all but the first element or
- * first `n` elements of an array. If a callback function is provided elements
- * at the beginning of the array are excluded from the result as long as the
- * callback returns truey. The callback is bound to `thisArg` and invoked
- * with three arguments; (value, index, array).
- *
- * If a property name is provided for `callback` the created "_.pluck" style
- * callback will return the property value of the given element.
- *
- * If an object is provided for `callback` the created "_.where" style callback
- * will return `true` for elements that have the properties of the given object,
- * else `false`.
- *
- * @static
- * @memberOf _
- * @alias drop, tail
+ * @alias tail
* @category Arrays
* @param {Array} array The array to query.
- * @param {Function|Object|number|string} [callback=1] The function called
- * per element or the number of elements to exclude. If a property name or
- * object is provided it will be used to create a "_.pluck" or "_.where"
- * style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a slice of `array`.
+ * @returns {Array} Returns the slice of `array`.
* @example
*
* _.rest([1, 2, 3]);
* // => [2, 3]
- *
- * // excludes the first two elements
- * _.rest([1, 2, 3], 2);
- * // => [3]
- *
- * // excludes elements from the beginning until the callback fails
- * _.rest([1, 2, 3], function(num) {
- * return num < 3;
- * });
- * // => [3]
- *
- * var characters = [
- * { 'name': 'barney', 'employer': 'slate', 'blocked': true },
- * { 'name': 'fred', 'employer': 'slate' },
- * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
- * ];
- *
- * // using "_.pluck" callback shorthand
- * _.pluck(_.rest(characters, 'blocked'), 'name');
- * // => ['fred', 'pebbles']
- *
- * // using "_.where" callback shorthand
- * _.rest(characters, { 'employer': 'slate' });
- * // => [{ 'name': 'pebbles', 'employer': 'na', 'blocked': true }]
*/
- function rest(array, callback, thisArg) {
- if (typeof callback != 'number' && callback != null) {
+ function rest(array, predicate, thisArg) {
+ if (typeof predicate != 'number' && predicate != null) {
var index = -1,
length = array ? array.length : 0,
n = 0;
- callback = lodash.createCallback(callback, thisArg, 3);
- while (++index < length && callback(array[index], index, array)) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ while (++index < length && predicate(array[index], index, array)) {
n++;
}
- } else if (callback == null || thisArg) {
+ } else if (predicate == null || thisArg) {
n = 1;
} else {
- n = callback > 0 ? callback : 0;
+ n = predicate < 0 ? 0 : predicate;
}
return slice(array, n);
}
@@ -3027,27 +3075,25 @@
* @param {Array} array The array to slice.
* @param {number} [start=0] The start index.
* @param {number} [end=array.length] The end index.
- * @returns {Array} Returns the new array.
+ * @returns {Array} Returns the slice of `array`.
*/
function slice(array, start, end) {
var index = -1,
length = array ? array.length : 0;
- if (typeof start == 'undefined') {
- start = 0;
- } else if (start < 0) {
+ start = typeof start == 'undefined' ? 0 : (+start || 0);
+ if (start < 0) {
start = nativeMax(length + start, 0);
} else if (start > length) {
start = length;
}
- if (typeof end == 'undefined') {
- end = length;
- } else if (end < 0) {
+ end = typeof end == 'undefined' ? length : (+end || 0);
+ if (end < 0) {
end = nativeMax(length + end, 0);
} else if (end > length) {
end = length;
}
- length = end - start || 0;
+ length = start > end ? 0 : (end - start);
var result = Array(length);
while (++index < length) {
@@ -3077,7 +3123,7 @@
* @param {*} value The value to evaluate.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {number} Returns the index at which `value` should be inserted
* into `array`.
@@ -3087,17 +3133,17 @@
* // => 2
*
* var dict = {
- * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'fourty': 40, 'fifty': 50 }
+ * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'forty': 40, 'fifty': 50 }
* };
*
* // using `callback`
- * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'forty', function(word) {
* return dict.wordToNumber[word];
* });
* // => 2
*
* // using `callback` with `thisArg`
- * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'fourty', function(word) {
+ * _.sortedIndex(['twenty', 'thirty', 'fifty'], 'forty', function(word) {
* return this.wordToNumber[word];
* }, dict);
* // => 2
@@ -3123,6 +3169,144 @@
return low;
}
+ /**
+ * Creates a slice of `array` with `n` elements taken from the beginning.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.take([1, 2, 3], 1);
+ * // => [1]
+ *
+ * _.take([1, 2, 3], 2);
+ * // => [1, 2]
+ *
+ * _.take([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.take([1, 2, 3], 0);
+ * // => []
+ */
+ var take = first;
+
+ /**
+ * Creates a slice of `array` with `n` elements taken from the end.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {number} [n=1] The number of elements to take.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRight([1, 2, 3], 1);
+ * // => [3]
+ *
+ * _.takeRight([1, 2, 3], 2);
+ * // => [2, 3]
+ *
+ * _.takeRight([1, 2, 3], 5);
+ * // => [1, 2, 3]
+ *
+ * _.takeRight([1, 2, 3], 0);
+ * // => []
+ */
+ var takeRight = last;
+
+ /**
+ * Creates a slice of `array` with elements taken from the end. Elements will
+ * be taken until the predicate returns falsey. The predicate is bound to
+ * `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per element.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeRightWhile([1, 2, 3], function(num) {
+ * return num > 1;
+ * });
+ * // => [2, 3]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'employer': 'slate' },
+ * { 'name': 'fred', 'employer': 'slate', 'blocked': true },
+ * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.takeRightWhile(characters, 'blocked'), 'name');
+ * // => ['fred', 'pebbles']
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.takeRightWhile(characters, { 'employer': 'na' }), 'name');
+ * // => ['pebbles']
+ */
+ var takeRightWhile = last;
+
+ /**
+ * Creates a slice of `array` with elements taken from the beginning. Elements
+ * will be taken until the predicate returns falsey. The predicate is bound
+ * to `thisArg` and invoked with three arguments; (value, index, array).
+ *
+ * If a property name is provided for `predicate` the created "_.pluck" style
+ * callback will return the property value of the given element.
+ *
+ * If an object is provided for `predicate` the created "_.where" style callback
+ * will return `true` for elements that have the properties of the given object,
+ * else `false`.
+ *
+ * @static
+ * @memberOf _
+ * @type Function
+ * @category Arrays
+ * @param {Array} array The array to query.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per element.
+ * @returns {Array} Returns the slice of `array`.
+ * @example
+ *
+ * _.takeWhile([1, 2, 3], function(num) {
+ * return num < 3;
+ * });
+ * // => [1, 2]
+ *
+ * var characters = [
+ * { 'name': 'barney', 'employer': 'slate', 'blocked': true },
+ * { 'name': 'fred', 'employer': 'slate' },
+ * { 'name': 'pebbles', 'employer': 'na', 'blocked': true }
+ * ];
+ *
+ * // using "_.pluck" callback shorthand
+ * _.pluck(_.takeWhile(characters, 'blocked'), 'name');
+ * // => ['barney']
+ *
+ * // using "_.where" callback shorthand
+ * _.pluck(_.takeWhile(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
+ */
+ var takeWhile = first;
+
/**
* Creates an array of unique values, in order, of the provided arrays using
* strict equality for comparisons, i.e. `===`.
@@ -3130,8 +3314,8 @@
* @static
* @memberOf _
* @category Arrays
- * @param {...Array} [array] The arrays to inspect.
- * @returns {Array} Returns an array of combined values.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of combined values.
* @example
*
* _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
@@ -3143,11 +3327,11 @@
/**
* Creates a duplicate-value-free version of an array using strict equality
- * for comparisons, i.e. `===`. If the array is sorted, providing
- * `true` for `isSorted` will use a faster algorithm. If a callback is provided
- * each element of `array` is passed through the callback before uniqueness
- * is computed. The callback is bound to `thisArg` and invoked with three
- * arguments; (value, index, array).
+ * for comparisons, i.e. `===`. If the array is sorted, providing `true` for
+ * `isSorted` will use a faster algorithm. If a callback is provided it will
+ * be executed for each value in the array to generate the criterion by which
+ * uniqueness is computed. The callback is bound to `thisArg` and invoked with
+ * three arguments; (value, index, array).
*
* If a property name is provided for `callback` the created "_.pluck" style
* callback will return the property value of the given element.
@@ -3162,11 +3346,11 @@
* @category Arrays
* @param {Array} array The array to process.
* @param {boolean} [isSorted=false] A flag to indicate that `array` is sorted.
- * @param {Function|Object|string} [callback=identity] The function called
- * per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Function|Object|string} [callback] The function called per iteration.
+ * If a property name or object is provided it will be used to create a "_.pluck"
+ * or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a duplicate-value-free array.
+ * @returns {Array} Returns the new duplicate-value-free array.
* @example
*
* _.uniq([1, 2, 1, 3, 1]);
@@ -3219,26 +3403,27 @@
* @memberOf _
* @category Arrays
* @param {Array} array The array to filter.
- * @param {...*} [value] The values to exclude.
- * @returns {Array} Returns a new array of filtered values.
+ * @param {...*} [values] The values to exclude.
+ * @returns {Array} Returns the new array of filtered values.
* @example
*
* _.without([1, 2, 1, 0, 3, 1, 4], 0, 1);
* // => [2, 3, 4]
*/
- function without(array) {
- return baseDifference(array, slice(arguments, 1));
+ function without() {
+ return baseDifference(arguments[0], slice(arguments, 1));
}
/**
* Creates an array that is the symmetric difference of the provided arrays.
- * See [Wikipedia](http://en.wikipedia.org/wiki/Symmetric_difference) for more details.
+ * See [Wikipedia](http://en.wikipedia.org/wiki/Symmetric_difference) for
+ * more details.
*
* @static
* @memberOf _
* @category Arrays
- * @param {...Array} [array] The arrays to inspect.
- * @returns {Array} Returns an array of values.
+ * @param {...Array} [arrays] The arrays to inspect.
+ * @returns {Array} Returns the new array of values.
* @example
*
* _.xor([1, 2, 3], [5, 2, 1, 4]);
@@ -3272,8 +3457,8 @@
* @memberOf _
* @alias unzip
* @category Arrays
- * @param {...Array} [array] The arrays to process.
- * @returns {Array} Returns a new array of grouped elements.
+ * @param {...Array} [arrays] The arrays to process.
+ * @returns {Array} Returns the array of grouped elements.
* @example
*
* _.zip(['fred', 'barney'], [30, 40], [true, false]);
@@ -3305,8 +3490,7 @@
* @category Arrays
* @param {Array} keys The array of keys.
* @param {Array} [values=[]] The array of values.
- * @returns {Object} Returns an object composed of the given keys and
- * corresponding values.
+ * @returns {Object} Returns the new object.
* @example
*
* _.zipObject(['fred', 'barney'], [30, 40]);
@@ -3341,7 +3525,7 @@
* @memberOf _
* @category Chaining
* @param {*} value The value to wrap.
- * @returns {Object} Returns the wrapper object.
+ * @returns {Object} Returns the new wrapper object.
* @example
*
* var characters = [
@@ -3358,9 +3542,7 @@
* // => 'pebbles is 1'
*/
function chain(value) {
- value = new lodashWrapper(value);
- value.__chain__ = true;
- return value;
+ return new lodashWrapper(value, true);
}
/**
@@ -3420,12 +3602,12 @@
}
/**
- * Produces the `toString` result of the wrapped value.
+ * Produces the result of coercing the wrapped value to a string.
*
* @name toString
* @memberOf _
* @category Chaining
- * @returns {string} Returns the string result.
+ * @returns {string} Returns the coerced string value.
* @example
*
* _([1, 2, 3]).toString();
@@ -3440,7 +3622,7 @@
*
* @name valueOf
* @memberOf _
- * @alias value
+ * @alias toJSON, value
* @category Chaining
* @returns {*} Returns the wrapped value.
* @example
@@ -3454,7 +3636,6 @@
/*--------------------------------------------------------------------------*/
-
/**
* Creates an array of elements from the specified indexes, or keys, of the
* `collection`. Indexes may be specified as individual arguments or as arrays
@@ -3464,10 +3645,9 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {...(number|number[]|string|string[])} [index] The indexes of `collection`
- * to retrieve, specified as individual indexes or arrays of indexes.
- * @returns {Array} Returns a new array of elements corresponding to the
- * provided indexes.
+ * @param {...(number|number[]|string|string[])} [keys] The keys of elements
+ * to pick, specified as individual keys or arrays of keys.
+ * @returns {Array} Returns the array of picked elements.
* @example
*
* _.at(['a', 'b', 'c', 'd', 'e'], [0, 2, 4]);
@@ -3476,17 +3656,11 @@
* _.at(['fred', 'barney', 'pebbles'], 0, 2);
* // => ['fred', 'pebbles']
*/
- function at(collection, guard) {
- var args = arguments,
- index = -1,
- props = baseFlatten(args, true, false, 1),
- length = props.length,
- type = typeof guard;
+ function at(collection) {
+ var index = -1,
+ props = baseFlatten(arguments, true, false, 1),
+ length = props.length;
- // enables use as a callback for functions like `_.map`
- if ((type == 'number' || type == 'string') && args[2] && args[2][guard] === collection) {
- length = 1;
- }
if (support.unindexedChars && isString(collection)) {
collection = collection.split('');
}
@@ -3506,10 +3680,10 @@
* @memberOf _
* @alias include
* @category Collections
- * @param {Array|Object|string} collection The collection to iterate over.
+ * @param {Array|Object|string} collection The collection to search.
* @param {*} target The value to check for.
* @param {number} [fromIndex=0] The index to search from.
- * @returns {boolean} Returns `true` if the `target` element is found, else `false`.
+ * @returns {boolean} Returns `true` if a matching element is found, else `false`.
* @example
*
* _.contains([1, 2, 3], 1);
@@ -3526,31 +3700,25 @@
*/
function contains(collection, target, fromIndex) {
var length = collection ? collection.length : 0;
- fromIndex = typeof fromIndex == 'number' ? fromIndex : 0;
-
- if (typeof length == 'number') {
+ if (!(typeof length == 'number' && length > -1 && length <= maxSafeInteger)) {
+ collection = values(collection);
+ length = collection.length;
+ }
+ if (typeof fromIndex == 'number') {
+ fromIndex = fromIndex < 0 ? nativeMax(length + fromIndex, 0) : (fromIndex || 0);
+ } else {
+ fromIndex = 0;
+ }
+ if (typeof collection == 'string' || !isArray(collection) && isString(collection)) {
if (fromIndex >= length) {
return false;
}
- if (typeof collection == 'string' || !isArray(collection) && isString(collection)) {
- return nativeContains
- ? nativeContains.call(collection, target, fromIndex)
- : collection.indexOf(target, fromIndex) > -1;
- }
- var indexOf = getIndexOf();
- fromIndex = (fromIndex < 0 ? nativeMax(0, length + fromIndex) : fromIndex) || 0;
- return indexOf(collection, target, fromIndex) > -1;
+ return nativeContains
+ ? nativeContains.call(collection, target, fromIndex)
+ : collection.indexOf(target, fromIndex) > -1;
}
- var index = -1,
- result = false;
-
- baseEach(collection, function(value) {
- if (++index >= fromIndex) {
- return !(result = value === target);
- }
- });
-
- return result;
+ var indexOf = getIndexOf();
+ return indexOf(collection, target, fromIndex) > -1;
}
/**
@@ -3573,7 +3741,7 @@
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
@@ -3592,14 +3760,14 @@
});
/**
- * Checks if the callback returns truey value for **all** elements of a
- * collection. The callback is bound to `thisArg` and invoked with three
- * arguments; (value, index|key, collection).
+ * Checks if the predicate returns truthy for **all** elements of a collection.
+ * The predicate is bound to `thisArg` and invoked with three arguments;
+ * (value, index|key, collection).
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -3608,11 +3776,11 @@
* @alias all
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {boolean} Returns `true` if all elements passed the callback check,
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {boolean} Returns `true` if all elements passed the predicate check,
* else `false`.
* @example
*
@@ -3632,36 +3800,36 @@
* _.every(characters, { 'age': 36 });
* // => false
*/
- function every(collection, callback, thisArg) {
+ function every(collection, predicate, thisArg) {
var result = true;
+ predicate = lodash.createCallback(predicate, thisArg, 3);
- callback = lodash.createCallback(callback, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
- if (!callback(collection[index], index, collection)) {
+ if (!predicate(collection[index], index, collection)) {
return false;
}
}
} else {
baseEach(collection, function(value, index, collection) {
- return (result = !!callback(value, index, collection));
+ return (result = !!predicate(value, index, collection));
});
}
return result;
}
/**
- * Iterates over elements of a collection, returning an array of all elements
- * the callback returns truey for. The callback is bound to `thisArg` and
+ * Iterates over elements of a collection returning an array of all elements
+ * the predicate returns truthy for. The predicate is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -3670,11 +3838,11 @@
* @alias select
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of elements that passed the callback check.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the new filtered array.
* @example
*
* var evens = _.filter([1, 2, 3, 4], function(num) { return num % 2 == 0; });
@@ -3693,23 +3861,23 @@
* _.filter(characters, { 'age': 36 });
* // => [{ 'name': 'barney', 'age': 36 }]
*/
- function filter(collection, callback, thisArg) {
+ function filter(collection, predicate, thisArg) {
var result = [];
+ predicate = lodash.createCallback(predicate, thisArg, 3);
- callback = lodash.createCallback(callback, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
var value = collection[index];
- if (callback(value, index, collection)) {
+ if (predicate(value, index, collection)) {
result.push(value);
}
}
} else {
baseEach(collection, function(value, index, collection) {
- if (callback(value, index, collection)) {
+ if (predicate(value, index, collection)) {
result.push(value);
}
});
@@ -3719,26 +3887,26 @@
/**
* Iterates over elements of a collection, returning the first element that
- * the callback returns truey for. The callback is bound to `thisArg` and
+ * the predicate returns truthy for. The predicate is bound to `thisArg` and
* invoked with three arguments; (value, index|key, collection).
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
* @static
* @memberOf _
- * @alias detect, findWhere
+ * @alias detect
* @category Collections
- * @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {*} Returns the found element, else `undefined`.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {*} Returns the matched element, else `undefined`.
* @example
*
* var characters = [
@@ -3760,43 +3928,28 @@
* _.find(characters, 'blocked');
* // => { 'name': 'fred', 'age': 40, 'blocked': true }
*/
- function find(collection, callback, thisArg) {
- callback = lodash.createCallback(callback, thisArg, 3);
+ function find(collection, predicate, thisArg) {
if (isArray(collection)) {
- var index = -1,
- length = collection.length;
-
- while (++index < length) {
- var value = collection[index];
- if (callback(value, index, collection)) {
- return value;
- }
- }
- } else {
- var result;
- baseEach(collection, function(value, index, collection) {
- if (callback(value, index, collection)) {
- result = value;
- return false;
- }
- });
- return result;
+ var index = findIndex(collection, predicate, thisArg);
+ return index > -1 ? collection[index] : undefined;
}
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return baseFind(collection, predicate, baseEach);
}
/**
- * This method is like `_.find` except that it iterates over elements
- * of a `collection` from right to left.
+ * This method is like `_.find` except that it iterates over elements of a
+ * collection from right to left.
*
* @static
* @memberOf _
* @category Collections
- * @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {*} Returns the found element, else `undefined`.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {*} Returns the matched element, else `undefined`.
* @example
*
* _.findLast([1, 2, 3, 4], function(num) {
@@ -3804,21 +3957,41 @@
* });
* // => 3
*/
- function findLast(collection, callback, thisArg) {
- var result;
-
- callback = lodash.createCallback(callback, thisArg, 3);
- baseEachRight(collection, function(value, index, collection) {
- if (callback(value, index, collection)) {
- result = value;
- return false;
- }
- });
- return result;
+ function findLast(collection, predicate, thisArg) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return baseFind(collection, predicate, baseEachRight);
}
/**
- * Iterates over elements of a collection, executing the callback for each
+ * Performs a deep comparison between each element in `collection` and the
+ * source object, returning the first element that has equivalent property
+ * values.
+ *
+ * @static
+ * @memberOf _
+ * @category Collections
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Object} source The object of property values to match.
+ * @returns {*} Returns the matched element, else `undefined`.
+ * @example
+ *
+ * var characters = [
+ * { 'name': 'barney', 'age': 36, 'employer': 'slate' },
+ * { 'name': 'fred', 'age': 40, 'employer': 'slate' }
+ * ];
+ *
+ * _.findWhere(characters, { 'employer': 'slate' });
+ * // => { 'name': 'barney', 'age': 36, 'employer': 'slate' }
+ *
+ * _.findWhere(characters, { 'age': 40 });
+ * // => { 'name': 'fred', 'age': 40, 'employer': 'slate' }
+ */
+ function findWhere(collection, source) {
+ return find(collection, matches(source));
+ }
+
+ /**
+ * Iterates over elements of a collection executing the callback for each
* element. The callback is bound to `thisArg` and invoked with three arguments;
* (value, index|key, collection). Callbacks may exit iteration early by
* explicitly returning `false`.
@@ -3844,24 +4017,14 @@
* // => logs each number and returns the object (property order is not guaranteed across environments)
*/
function forEach(collection, callback, thisArg) {
- if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
- var index = -1,
- length = collection.length;
-
- while (++index < length) {
- if (callback(collection[index], index, collection) === false) {
- break;
- }
- }
- } else {
- baseEach(collection, baseCreateCallback(callback, thisArg, 3));
- }
- return collection;
+ return (callback && typeof thisArg == 'undefined' && isArray(collection))
+ ? arrayEach(collection, callback)
+ : baseEach(collection, baseCreateCallback(callback, thisArg, 3));
}
/**
- * This method is like `_.forEach` except that it iterates over elements
- * of a `collection` from right to left.
+ * This method is like `_.forEach` except that it iterates over elements of
+ * a collection from right to left.
*
* @static
* @memberOf _
@@ -3877,17 +4040,9 @@
* // => logs each number from right to left and returns '3,2,1'
*/
function forEachRight(collection, callback, thisArg) {
- if (callback && typeof thisArg == 'undefined' && isArray(collection)) {
- var length = collection.length;
- while (length--) {
- if (callback(collection[length], length, collection) === false) {
- break;
- }
- }
- } else {
- baseEachRight(collection, baseCreateCallback(callback, thisArg, 3));
- }
- return collection;
+ return (callback && typeof thisArg == 'undefined' && isArray(collection))
+ ? arrayEachRight(collection, callback)
+ : baseEachRight(collection, baseCreateCallback(callback, thisArg, 3));
}
/**
@@ -3910,7 +4065,7 @@
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
@@ -3953,23 +4108,23 @@
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the composed aggregate object.
* @example
*
- * var keys = [
+ * var keyData = [
* { 'dir': 'left', 'code': 97 },
* { 'dir': 'right', 'code': 100 }
* ];
*
- * _.indexBy(keys, 'dir');
+ * _.indexBy(keyData, 'dir');
* // => { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }
*
- * _.indexBy(keys, function(key) { return String.fromCharCode(key.code); });
+ * _.indexBy(keyData, function(object) { return String.fromCharCode(object.code); });
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*
- * _.indexBy(keys, function(key) { return this.fromCharCode(key.code); }, String);
+ * _.indexBy(keyData, function(object) { return this.fromCharCode(object.code); }, String);
* // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }
*/
var indexBy = createAggregator(function(result, value, key) {
@@ -3977,10 +4132,10 @@
});
/**
- * Invokes the method named by `methodName` on each element in the `collection`
+ * Invokes the method named by `methodName` on each element in the collection
* returning an array of the results of each invoked method. Additional arguments
* will be provided to each invoked method. If `methodName` is a function it
- * will be invoked for, and `this` bound to, each element in the `collection`.
+ * will be invoked for, and `this` bound to, each element in the collection.
*
* @static
* @memberOf _
@@ -3989,7 +4144,7 @@
* @param {Function|string} methodName The name of the method to invoke or
* the function invoked per iteration.
* @param {...*} [args] Arguments to invoke the method with.
- * @returns {Array} Returns a new array of the results of each invoked method.
+ * @returns {Array} Returns the array of results.
* @example
*
* _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
@@ -3999,22 +4154,16 @@
* // => [['1', '2', '3'], ['4', '5', '6']]
*/
function invoke(collection, methodName) {
- var index = -1,
+ var args = slice(arguments, 2),
+ index = -1,
isFunc = typeof methodName == 'function',
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
+ length = collection && collection.length,
+ result = Array(length < 0 ? 0 : length >>> 0);
- if (arguments.length < 3 && isArray(collection)) {
- while (++index < length) {
- var value = collection[index];
- result[index] = isFunc ? methodName.call(value) : value[methodName]();
- }
- } else {
- var args = slice(arguments, 2);
- baseEach(collection, function(value) {
- result[++index] = (isFunc ? methodName : value[methodName]).apply(value, args);
- });
- }
+ baseEach(collection, function(value) {
+ var func = isFunc ? methodName : (value != null && value[methodName]);
+ result[++index] = func ? func.apply(value, args) : undefined;
+ });
return result;
}
@@ -4037,9 +4186,9 @@
* @param {Array|Object|string} collection The collection to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of the results of each `callback` execution.
+ * @returns {Array} Returns the new mapped array.
* @example
*
* _.map([1, 2, 3], function(num) { return num * 3; });
@@ -4058,20 +4207,17 @@
* // => ['barney', 'fred']
*/
function map(collection, callback, thisArg) {
- var index = -1,
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
-
callback = lodash.createCallback(callback, thisArg, 3);
+
if (isArray(collection)) {
- while (++index < length) {
- result[index] = callback(collection[index], index, collection);
- }
- } else {
- baseEach(collection, function(value, key, collection) {
- result[++index] = callback(value, key, collection);
- });
+ return arrayMap(collection, callback, thisArg);
}
+ var index = -1,
+ result = [];
+
+ baseEach(collection, function(value, key, collection) {
+ result[++index] = callback(value, key, collection);
+ });
return result;
}
@@ -4093,9 +4239,9 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
- * per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Function|Object|string} [callback] The function called per iteration.
+ * If a property name or object is provided it will be used to create a "_.pluck"
+ * or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the maximum value.
* @example
@@ -4103,6 +4249,9 @@
* _.max([4, 2, 8, 6]);
* // => 8
*
+ * _.max([]);
+ * // => -Infinity
+ *
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
@@ -4141,7 +4290,7 @@
baseEach(collection, function(value, index, collection) {
var current = callback(value, index, collection);
- if (current > computed) {
+ if (current > computed || (current === -Infinity && current === result)) {
computed = current;
result = value;
}
@@ -4168,9 +4317,9 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
- * per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Function|Object|string} [callback] The function called per iteration.
+ * If a property name or object is provided it will be used to create a "_.pluck"
+ * or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {*} Returns the minimum value.
* @example
@@ -4178,6 +4327,9 @@
* _.min([4, 2, 8, 6]);
* // => 2
*
+ * _.min([]);
+ * // => Infinity
+ *
* var characters = [
* { 'name': 'barney', 'age': 36 },
* { 'name': 'fred', 'age': 40 }
@@ -4216,7 +4368,7 @@
baseEach(collection, function(value, index, collection) {
var current = callback(value, index, collection);
- if (current < computed) {
+ if (current < computed || (current === Infinity && current === result)) {
computed = current;
result = value;
}
@@ -4227,14 +4379,14 @@
/**
* Creates an array of elements split into two groups, the first of which
- * contains elements the callback returns truey for, while the second of which
- * contains elements the callback returns falsey for. The callback is bound
+ * contains elements the predicate returns truthy for, while the second of which
+ * contains elements the predicate returns falsey for. The predicate is bound
* to `thisArg` and invoked with three arguments; (value, index|key, collection).
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -4242,11 +4394,11 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of grouped elements.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the array of grouped elements.
* @example
*
* _.partition([1, 2, 3], function(num) { return num % 2; });
@@ -4271,18 +4423,17 @@
*/
var partition = createAggregator(function(result, value, key) {
result[key ? 0 : 1].push(value);
- }, true);
+ }, function() { return [[], []]; });
/**
* Retrieves the value of a specified property from all elements in the collection.
*
* @static
* @memberOf _
- * @type Function
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
* @param {string} key The name of the property to pluck.
- * @returns {Array} Returns a new array of property values.
+ * @returns {Array} Returns the property values.
* @example
*
* var characters = [
@@ -4293,7 +4444,9 @@
* _.pluck(characters, 'name');
* // => ['barney', 'fred']
*/
- var pluck = map;
+ function pluck(collection, key) {
+ return map(collection, property(key));
+ }
/**
* Reduces a collection to a value which is the accumulated result of running
@@ -4327,8 +4480,8 @@
*/
function reduce(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
-
callback = lodash.createCallback(callback, thisArg, 4);
+
if (isArray(collection)) {
var index = -1,
length = collection.length;
@@ -4350,8 +4503,8 @@
}
/**
- * This method is like `_.reduce` except that it iterates over elements
- * of a `collection` from right to left.
+ * This method is like `_.reduce` except that it iterates over elements of a
+ * collection from right to left.
*
* @static
* @memberOf _
@@ -4364,14 +4517,14 @@
* @returns {*} Returns the accumulated value.
* @example
*
- * var list = [[0, 1], [2, 3], [4, 5]];
- * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
+ * var array = [[0, 1], [2, 3], [4, 5]];
+ * _.reduceRight(array, function(flattened, other) { return flattened.concat(other); }, []);
* // => [4, 5, 2, 3, 0, 1]
*/
function reduceRight(collection, callback, accumulator, thisArg) {
var noaccum = arguments.length < 3;
-
callback = lodash.createCallback(callback, thisArg, 4);
+
baseEachRight(collection, function(value, index, collection) {
accumulator = noaccum
? (noaccum = false, value)
@@ -4381,13 +4534,13 @@
}
/**
- * The opposite of `_.filter`; this method returns the elements of a
- * collection that the callback does **not** return truey for.
+ * The opposite of `_.filter`; this method returns the elements of a collection
+ * the predicate does **not** return truthy for.
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -4395,11 +4548,11 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of elements that failed the callback check.
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Array} Returns the new filtered array.
* @example
*
* var odds = _.reject([1, 2, 3, 4], function(num) { return num % 2 == 0; });
@@ -4418,11 +4571,9 @@
* _.reject(characters, { 'age': 36 });
* // => [{ 'name': 'fred', 'age': 40, 'blocked': true }]
*/
- function reject(collection, callback, thisArg) {
- callback = lodash.createCallback(callback, thisArg, 3);
- return filter(collection, function(value, index, collection) {
- return !callback(value, index, collection);
- });
+ function reject(collection, predicate, thisArg) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return filter(collection, negate(predicate));
}
/**
@@ -4434,7 +4585,7 @@
* @param {Array|Object|string} collection The collection to sample.
* @param {number} [n] The number of elements to sample.
* @param- {Object} [guard] Enables use as a callback for functions like `_.map`.
- * @returns {*} Returns the random sample(s) of `collection`.
+ * @returns {*} Returns the random sample(s).
* @example
*
* _.sample([1, 2, 3, 4]);
@@ -4450,22 +4601,24 @@
collection = collection.split('');
}
if (n == null || guard) {
- return collection ? collection[baseRandom(0, collection.length - 1)] : undefined;
+ var length = collection ? collection.length : 0;
+ return length > 0 ? collection[baseRandom(0, length - 1)] : undefined;
}
var result = shuffle(collection);
- result.length = nativeMin(nativeMax(0, n), result.length);
+ result.length = nativeMin(n < 0 ? 0 : (+n || 0), result.length);
return result;
}
/**
* Creates an array of shuffled values, using a version of the Fisher-Yates
- * shuffle. See [Wikipedia](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle) for more details.
+ * shuffle. See [Wikipedia](http://en.wikipedia.org/wiki/Fisher-Yates_shuffle)
+ * for more details.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to shuffle.
- * @returns {Array} Returns a new shuffled collection.
+ * @returns {Array} Returns the new shuffled array.
* @example
*
* _.shuffle([1, 2, 3, 4]);
@@ -4473,20 +4626,19 @@
*/
function shuffle(collection) {
var index = -1,
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
+ length = collection && collection.length,
+ result = Array(length < 0 ? 0 : length >>> 0);
baseEach(collection, function(value) {
var rand = baseRandom(0, ++index);
result[index] = result[rand];
result[rand] = value;
});
-
return result;
}
/**
- * Gets the size of the `collection` by returning `collection.length` for arrays
+ * Gets the size of the collection by returning `collection.length` for arrays
* and array-like objects or the number of own enumerable properties for objects.
*
* @static
@@ -4507,19 +4659,21 @@
*/
function size(collection) {
var length = collection ? collection.length : 0;
- return typeof length == 'number' ? length : keys(collection).length;
+ return (typeof length == 'number' && length > -1 && length <= maxSafeInteger)
+ ? length
+ : keys(collection).length;
}
/**
- * Checks if the callback returns a truey value for **any** element of a
- * collection. The function returns as soon as it finds a passing value and
- * does not iterate over the entire collection. The callback is bound to
- * `thisArg` and invoked with three arguments; (value, index|key, collection).
+ * Checks if the predicate returns truthy for **any** element of a collection.
+ * The function returns as soon as it finds a passing value and does not iterate
+ * over the entire collection. The predicate is bound to `thisArg` and invoked
+ * with three arguments; (value, index|key, collection).
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -4528,11 +4682,11 @@
* @alias any
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Function|Object|string} [callback=identity] The function called
+ * @param {Function|Object|string} [predicate=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {boolean} Returns `true` if any element passed the callback check,
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {boolean} Returns `true` if any element passed the predicate check,
* else `false`.
* @example
*
@@ -4552,22 +4706,22 @@
* _.some(characters, { 'age': 1 });
* // => false
*/
- function some(collection, callback, thisArg) {
+ function some(collection, predicate, thisArg) {
var result;
+ predicate = lodash.createCallback(predicate, thisArg, 3);
- callback = lodash.createCallback(callback, thisArg, 3);
if (isArray(collection)) {
var index = -1,
length = collection.length;
while (++index < length) {
- if (callback(collection[index], index, collection)) {
+ if (predicate(collection[index], index, collection)) {
return true;
}
}
} else {
baseEach(collection, function(value, index, collection) {
- return !(result = callback(value, index, collection));
+ return !(result = predicate(value, index, collection));
});
}
return !!result;
@@ -4594,11 +4748,11 @@
* @memberOf _
* @category Collections
* @param {Array|Object|string} collection The collection to iterate over.
- * @param {Array|Function|Object|string} [callback=identity] The function called
- * per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * @param {Array|Function|Object|string} [callback=identity] The function
+ * called per iteration. If a property name or object is provided it will
+ * be used to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns a new array of sorted elements.
+ * @returns {Array} Returns the new sorted array.
* @example
*
* _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
@@ -4624,9 +4778,9 @@
*/
function sortBy(collection, callback, thisArg) {
var index = -1,
+ length = collection && collection.length,
multi = callback && isArray(callback),
- length = collection ? collection.length : 0,
- result = Array(typeof length == 'number' ? length : 0);
+ result = Array(length < 0 ? 0 : length >>> 0);
if (!multi) {
callback = lodash.createCallback(callback, thisArg, 3);
@@ -4654,7 +4808,7 @@
}
/**
- * Converts the `collection` to an array.
+ * Converts `collection` to an array.
*
* @static
* @memberOf _
@@ -4667,7 +4821,8 @@
* // => [2, 3, 4]
*/
function toArray(collection) {
- if (collection && typeof collection.length == 'number') {
+ var length = collection && collection.length;
+ if (typeof length == 'number' && length > -1 && length <= maxSafeInteger) {
return (support.unindexedChars && isString(collection))
? collection.split('')
: slice(collection);
@@ -4677,35 +4832,39 @@
/**
* Performs a deep comparison between each element in `collection` and the
- * `source` object, returning an array of all elements that have equivalent
+ * source object, returning an array of all elements that have equivalent
* property values.
*
* @static
* @memberOf _
- * @type Function
* @category Collections
- * @param {Array|Object|string} collection The collection to iterate over.
- * @param {Object} source The object of property values to filter by.
- * @returns {Array} Returns a new array of elements that have the given properties.
+ * @param {Array|Object|string} collection The collection to search.
+ * @param {Object} source The object of property values to match.
+ * @returns {Array} Returns the new filtered array.
* @example
*
* var characters = [
- * { 'name': 'barney', 'age': 36, 'pets': ['hoppy'] },
- * { 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }
+ * { 'name': 'barney', 'age': 36, 'employer': 'slate', 'pets': ['hoppy'] },
+ * { 'name': 'fred', 'age': 40, 'employer': 'slate', 'pets': ['baby puss', 'dino'] }
* ];
*
- * _.where(characters, { 'age': 36 });
- * // => [{ 'name': 'barney', 'age': 36, 'pets': ['hoppy'] }]
+ * _.pluck(_.where(characters, { 'age': 36 }), 'name');
+ * // => ['barney']
*
- * _.where(characters, { 'pets': ['dino'] });
- * // => [{ 'name': 'fred', 'age': 40, 'pets': ['baby puss', 'dino'] }]
+ * _.pluck(_.where(characters, { 'pets': ['dino'] }), 'name');
+ * // => ['fred']
+ *
+ * _.pluck(_.where(characters, { 'employer': 'slate' }), 'name');
+ * // => ['barney', 'fred']
*/
- var where = filter;
+ function where(collection, source) {
+ return filter(collection, matches(source));
+ }
/*--------------------------------------------------------------------------*/
/**
- * Creates a function that executes `func`, with the `this` binding and
+ * Creates a function that executes `func`, with the `this` binding and
* arguments of the created function, only after being called `n` times.
*
* @static
@@ -4730,8 +4889,9 @@
*/
function after(n, func) {
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
+ n = nativeIsFinite(n = +n) ? n : 0;
return function() {
if (--n < 1) {
return func.apply(this, arguments);
@@ -4740,9 +4900,9 @@
}
/**
- * Creates a function that, when called, invokes `func` with the `this`
- * binding of `thisArg` and prepends any additional `bind` arguments to those
- * provided to the bound function.
+ * Creates a function that invokes `func` with the `this` binding of `thisArg`
+ * and prepends any additional `bind` arguments to those provided to the bound
+ * function.
*
* Note: Unlike native `Function#bind` this method does not set the `length`
* property of bound functions.
@@ -4789,8 +4949,8 @@
* @memberOf _
* @category Functions
* @param {Object} object The object to bind and assign the bound methods to.
- * @param {...string} [methodName] The object method names to
- * bind, specified as individual method names or arrays of method names.
+ * @param {...string} [methodNames] The object method names to bind, specified
+ * as individual method names or arrays of method names.
* @returns {Object} Returns `object`.
* @example
*
@@ -4816,10 +4976,10 @@
}
/**
- * Creates a function that, when called, invokes the method at `object[key]`
- * and prepends any additional `bindKey` arguments to those provided to the bound
- * function. This method differs from `_.bind` by allowing bound functions to
- * reference methods that will be redefined or don't yet exist.
+ * Creates a function that invokes the method at `object[key]` and prepends
+ * any additional `bindKey` arguments to those provided to the bound function.
+ * This method differs from `_.bind` by allowing bound functions to reference
+ * methods that will be redefined or don't yet exist.
* See [Peter Michaux's article](http://michaux.ca/articles/lazy-function-definition-pattern)
* for more details.
*
@@ -4865,7 +5025,7 @@
* @static
* @memberOf _
* @category Functions
- * @param {...Function} [func] Functions to compose.
+ * @param {...Function} [funcs] Functions to compose.
* @returns {Function} Returns the new composed function.
* @example
*
@@ -4893,7 +5053,7 @@
while (length--) {
if (!isFunction(funcs[length])) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
}
return function() {
@@ -4951,7 +5111,7 @@
* the leading and/or trailing edge of the `wait` timeout. Subsequent calls
* to the debounced function will return the result of the last `func` call.
*
- * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * Note: If `leading` and `trailing` options are `true`, `func` will be called
* on the trailing edge of the timeout only if the the debounced function is
* invoked more than once during the `wait` timeout.
*
@@ -4996,15 +5156,15 @@
trailing = true;
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
- wait = nativeMax(0, wait) || 0;
+ wait = wait < 0 ? 0 : wait;
if (options === true) {
var leading = true;
trailing = false;
} else if (isObject(options)) {
leading = options.leading;
- maxWait = 'maxWait' in options && (nativeMax(wait, options.maxWait) || 0);
+ maxWait = 'maxWait' in options && nativeMax(wait, +options.maxWait || 0);
trailing = 'trailing' in options ? options.trailing : trailing;
}
var delayed = function() {
@@ -5101,7 +5261,7 @@
*/
function defer(func) {
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
var args = slice(arguments, 1);
return setTimeout(function() { func.apply(undefined, args); }, 1);
@@ -5125,7 +5285,7 @@
*/
function delay(func, wait) {
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
var args = slice(arguments, 2);
return setTimeout(function() { func.apply(undefined, args); }, wait);
@@ -5143,7 +5303,7 @@
* @memberOf _
* @category Functions
* @param {Function} func The function to have its output memoized.
- * @param {Function} [resolver] A function used to resolve the cache key.
+ * @param {Function} [resolver] The function to resolve the cache key.
* @returns {Function} Returns the new memoizing function.
* @example
*
@@ -5169,8 +5329,8 @@
* // => { 'name': 'penelope', 'age': 1 }
*/
function memoize(func, resolver) {
- if (!isFunction(func)) {
- throw new TypeError;
+ if (!isFunction(func) || (resolver && !isFunction(resolver))) {
+ throw new TypeError(funcErrorText);
}
var memoized = function() {
var cache = memoized.cache,
@@ -5184,6 +5344,34 @@
return memoized;
}
+ /**
+ * Creates a function that negates the result of the predicate `func`. The
+ * `func` function is executed with the `this` binding and arguments of the
+ * created function.
+ *
+ * @static
+ * @memberOf _
+ * @category Functions
+ * @param {Function} predicate The predicate to negate.
+ * @returns {Function} Returns the new function.
+ * @example
+ *
+ * function isEven(num) {
+ * return num % 2 == 0;
+ * }
+ *
+ * _.filter([1, 2, 3, 4, 5, 6], _.negate(isEven));
+ * // => [1, 3, 5]
+ */
+ function negate(predicate) {
+ if (!isFunction(predicate)) {
+ throw new TypeError(funcErrorText);
+ }
+ return function() {
+ return !predicate.apply(this, arguments);
+ };
+ }
+
/**
* Creates a function that is restricted to execute `func` once. Repeat calls to
* the function will return the value of the first call. The `func` is executed
@@ -5206,7 +5394,7 @@
result;
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
return function() {
if (ran) {
@@ -5222,9 +5410,9 @@
}
/**
- * Creates a function that, when called, invokes `func` with any additional
- * `partial` arguments prepended to those provided to the new function. This
- * method is similar to `_.bind` except it does **not** alter the `this` binding.
+ * Creates a function that invokes `func` with any additional `partial` arguments
+ * prepended to those provided to the new function. This method is similar to
+ * `_.bind` except it does **not** alter the `this` binding.
*
* Note: This method does not set the `length` property of partially applied
* functions.
@@ -5267,20 +5455,15 @@
* @returns {Function} Returns the new partially applied function.
* @example
*
- * var defaultsDeep = _.partialRight(_.merge, _.defaults);
+ * var defaultsDeep = _.partialRight(_.merge, function deep(value, other) {
+ * return _.merge(value, other, deep);
+ * });
*
- * var options = {
- * 'variable': 'data',
- * 'imports': { 'jq': $ }
- * };
+ * var object = { 'a': { 'b': { 'c': 1 } } },
+ * source = { 'a': { 'b': { 'c': 2, 'd': 2 } } };
*
- * defaultsDeep(options, _.templateSettings);
- *
- * options.variable
- * // => 'data'
- *
- * options.imports
- * // => { '_': _, 'jq': $ }
+ * defaultsDeep(object, source);
+ * // => { 'a': { 'b': { 'c': 1, 'd': 2 } } }
*/
function partialRight(func) {
if (func) {
@@ -5299,7 +5482,7 @@
* of the `wait` timeout. Subsequent calls to the throttled function will
* return the result of the last `func` call.
*
- * Note: If `leading` and `trailing` options are `true` `func` will be called
+ * Note: If `leading` and `trailing` options are `true`, `func` will be called
* on the trailing edge of the timeout only if the the throttled function is
* invoked more than once during the `wait` timeout.
*
@@ -5328,16 +5511,16 @@
trailing = true;
if (!isFunction(func)) {
- throw new TypeError;
+ throw new TypeError(funcErrorText);
}
if (options === false) {
leading = false;
} else if (isObject(options)) {
- leading = 'leading' in options ? options.leading : leading;
- trailing = 'trailing' in options ? options.trailing : trailing;
+ leading = 'leading' in options ? !!options.leading : leading;
+ trailing = 'trailing' in options ? !!options.trailing : trailing;
}
debounceOptions.leading = leading;
- debounceOptions.maxWait = wait;
+ debounceOptions.maxWait = +wait;
debounceOptions.trailing = trailing;
return debounce(func, wait, debounceOptions);
@@ -5370,7 +5553,6 @@
/*--------------------------------------------------------------------------*/
-
/**
* Assigns own enumerable properties of source object(s) to the destination
* object. Subsequent sources will overwrite property assignments of previous
@@ -5383,7 +5565,7 @@
* @alias extend
* @category Objects
* @param {Object} object The destination object.
- * @param {...Object} [source] The source objects.
+ * @param {...Object} [sources] The source objects.
* @param {Function} [callback] The function to customize assigning values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
@@ -5392,16 +5574,19 @@
* _.assign({ 'name': 'fred' }, { 'employer': 'slate' });
* // => { 'name': 'fred', 'employer': 'slate' }
*
- * var defaults = _.partialRight(_.assign, function(a, b) {
- * return typeof a == 'undefined' ? b : a;
+ * var defaults = _.partialRight(_.assign, function(value, other) {
+ * return typeof value == 'undefined' ? other : value;
* });
*
* defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' });
* // => { 'name': 'barney', 'employer': 'slate' }
*/
function assign(object, source, guard) {
- var args = arguments,
- argsIndex = 0,
+ var args = arguments;
+ if (!object || args.length < 2) {
+ return object;
+ }
+ var argsIndex = 0,
argsLength = args.length,
type = typeof guard;
@@ -5417,15 +5602,13 @@
}
while (++argsIndex < argsLength) {
source = args[argsIndex];
- if (isObject(source)) {
- var index = -1,
- props = keys(source),
- length = props.length;
+ var index = -1,
+ props = keys(source),
+ length = props.length;
- while (++index < length) {
- var key = props[index];
- object[key] = callback ? callback(object[key], source[key]) : source[key];
- }
+ while (++index < length) {
+ var key = props[index];
+ object[key] = callback ? callback(object[key], source[key]) : source[key];
}
}
return object;
@@ -5583,11 +5766,14 @@
* object for all destination properties that resolve to `undefined`. Once a
* property is set, additional defaults of the same property will be ignored.
*
+ * Note: See the [documentation example of `_.partialRight`](http://lodash.com/docs#partialRight)
+ * for a deep version of this method.
+ *
* @static
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
- * @param {...Object} [source] The source objects.
+ * @param {...Object} [sources] The source objects.
* @param- {Object} [guard] Enables use as a callback for functions like `_.reduce`.
* @returns {Object} Returns the destination object.
* @example
@@ -5595,42 +5781,23 @@
* _.defaults({ 'name': 'barney' }, { 'name': 'fred', 'employer': 'slate' });
* // => { 'name': 'barney', 'employer': 'slate' }
*/
- function defaults(object, source, guard) {
- var args = arguments,
- argsIndex = 0,
- argsLength = args.length,
- type = typeof guard;
-
- // enables use as a callback for functions like `_.reduce`
- if ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) {
- argsLength = 2;
+ function defaults(object) {
+ if (!object) {
+ return object;
}
- while (++argsIndex < argsLength) {
- source = args[argsIndex];
- if (isObject(source)) {
- var index = -1,
- props = keys(source),
- length = props.length;
-
- while (++index < length) {
- var key = props[index];
- if (typeof object[key] == 'undefined') {
- object[key] = source[key];
- }
- }
- }
- }
- return object;
+ var args = slice(arguments);
+ args.push(assignDefaults);
+ return assign.apply(null, args);
}
/**
* This method is like `_.findIndex` except that it returns the key of the
- * first element that passes the callback check, instead of the element itself.
+ * first element the predicate returns truthy for, instead of the element itself.
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -5638,11 +5805,11 @@
* @memberOf _
* @category Objects
* @param {Object} object The object to search.
- * @param {Function|Object|string} [callback=identity] The function called per
- * iteration. If a property name or object is provided it will be used to
- * create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
* @example
*
* var characters = {
@@ -5664,27 +5831,19 @@
* _.findKey(characters, 'blocked');
* // => 'fred'
*/
- function findKey(object, callback, thisArg) {
- var result;
-
- callback = lodash.createCallback(callback, thisArg, 3);
- baseForOwn(object, function(value, key, object) {
- if (callback(value, key, object)) {
- result = key;
- return false;
- }
- });
- return result;
+ function findKey(object, predicate, thisArg) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return baseFind(object, predicate, baseForOwn, true);
}
/**
- * This method is like `_.findKey` except that it iterates over elements
- * of a `collection` in the opposite order.
+ * This method is like `_.findKey` except that it iterates over elements of
+ * a collection in the opposite order.
*
- * If a property name is provided for `callback` the created "_.pluck" style
+ * If a property name is provided for `predicate` the created "_.pluck" style
* callback will return the property value of the given element.
*
- * If an object is provided for `callback` the created "_.where" style callback
+ * If an object is provided for `predicate` the created "_.where" style callback
* will return `true` for elements that have the properties of the given object,
* else `false`.
*
@@ -5692,11 +5851,11 @@
* @memberOf _
* @category Objects
* @param {Object} object The object to search.
- * @param {Function|Object|string} [callback=identity] The function called per
- * iteration. If a property name or object is provided it will be used to
- * create a "_.pluck" or "_.where" style callback, respectively.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {string|undefined} Returns the key of the found element, else `undefined`.
+ * @param {Function|Object|string} [predicate=identity] The function called
+ * per iteration. If a property name or object is provided it will be used
+ * to create a "_.pluck" or "_.where" style callback respectively.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {string|undefined} Returns the key of the matched element, else `undefined`.
* @example
*
* var characters = {
@@ -5718,28 +5877,19 @@
* _.findLastKey(characters, 'blocked');
* // => 'pebbles'
*/
- function findLastKey(object, callback, thisArg) {
- var result;
-
- callback = lodash.createCallback(callback, thisArg, 3);
- baseForOwnRight(object, function(value, key, object) {
- if (callback(value, key, object)) {
- result = key;
- return false;
- }
- });
- return result;
+ function findLastKey(object, predicate, thisArg) {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return baseFind(object, predicate, baseForOwnRight, true);
}
/**
- * Iterates over own and inherited enumerable properties of an object,
- * executing the callback for each property. The callback is bound to `thisArg`
- * and invoked with three arguments; (value, key, object). Callbacks may exit
- * iteration early by explicitly returning `false`.
+ * Iterates over own and inherited enumerable properties of an object executing
+ * the callback for each property. The callback is bound to `thisArg` and invoked
+ * with three arguments; (value, key, object). Callbacks may exit iteration
+ * early by explicitly returning `false`.
*
* @static
* @memberOf _
- * @type Function
* @category Objects
* @param {Object} object The object to iterate over.
* @param {Function} [callback=identity] The function called per iteration.
@@ -5752,24 +5902,21 @@
* this.y = 0;
* }
*
- * Shape.prototype.move = function(x, y) {
- * this.x += x;
- * this.y += y;
- * };
+ * Shape.prototype.z = 0;
*
* _.forIn(new Shape, function(value, key) {
* console.log(key);
* });
- * // => logs 'x', 'y', and 'move' (property order is not guaranteed across environments)
+ * // => logs 'x', 'y', and 'z' (property order is not guaranteed across environments)
*/
function forIn(object, callback, thisArg) {
callback = callback && typeof thisArg == 'undefined' ? callback : baseCreateCallback(callback, thisArg, 3);
- return baseForIn(object, callback);
+ return baseFor(object, callback, keysIn);
}
/**
- * This method is like `_.forIn` except that it iterates over elements
- * of a `collection` in the opposite order.
+ * This method is like `_.forIn` except that it iterates over elements of a
+ * collection in the opposite order.
*
* @static
* @memberOf _
@@ -5785,34 +5932,20 @@
* this.y = 0;
* }
*
- * Shape.prototype.move = function(x, y) {
- * this.x += x;
- * this.y += y;
- * };
+ * Shape.prototype.z = 0;
*
* _.forInRight(new Shape, function(value, key) {
* console.log(key);
* });
- * // => logs 'move', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'move'
+ * // => logs 'z', 'y', and 'x' assuming `_.forIn ` logs 'x', 'y', and 'z'
*/
function forInRight(object, callback, thisArg) {
- var pairs = [];
- baseForIn(object, function(value, key) {
- pairs.push(key, value);
- });
-
- var length = pairs.length;
callback = baseCreateCallback(callback, thisArg, 3);
- while (length--) {
- if (callback(pairs[length--], pairs[length], object) === false) {
- break;
- }
- }
- return object;
+ return baseForRight(object, callback, keysIn);
}
/**
- * Iterates over own enumerable properties of an object, executing the callback
+ * Iterates over own enumerable properties of an object executing the callback
* for each property. The callback is bound to `thisArg` and invoked with three
* arguments; (value, key, object). Callbacks may exit iteration early by
* explicitly returning `false`.
@@ -5837,8 +5970,8 @@
}
/**
- * This method is like `_.forOwn` except that it iterates over elements
- * of a `collection` in the opposite order.
+ * This method is like `_.forOwn` except that it iterates over elements of a
+ * collection in the opposite order.
*
* @static
* @memberOf _
@@ -5855,17 +5988,8 @@
* // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length'
*/
function forOwnRight(object, callback, thisArg) {
- var props = keys(object),
- length = props.length;
-
callback = baseCreateCallback(callback, thisArg, 3);
- while (length--) {
- var key = props[length];
- if (callback(object[key], key, object) === false) {
- break;
- }
- }
- return object;
+ return baseForRight(object, callback, keys);
}
/**
@@ -5877,7 +6001,7 @@
* @alias methods
* @category Objects
* @param {Object} object The object to inspect.
- * @returns {Array} Returns an array of property names that have function values.
+ * @returns {Array} Returns the new sorted array of property names.
* @example
*
* _.functions(_);
@@ -5885,6 +6009,7 @@
*/
function functions(object) {
var result = [];
+
baseForIn(object, function(value, key) {
if (isFunction(value)) {
result.push(key);
@@ -5923,7 +6048,7 @@
* @category Objects
* @param {Object} object The object to invert.
* @param {boolean} [multiValue=false] Allow multiple values per key.
- * @returns {Object} Returns the created inverted object.
+ * @returns {Object} Returns the new inverted object.
* @example
*
* _.invert({ 'first': 'fred', 'second': 'barney' });
@@ -5961,26 +6086,53 @@
return result;
}
+ /**
+ * Checks if `value` is an `arguments` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`.
+ * @example
+ *
+ * (function() { return _.isArguments(arguments); })();
+ * // => true
+ *
+ * _.isArguments([1, 2, 3]);
+ * // => false
+ */
+ function isArguments(value) {
+ return (value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == argsClass) || false;
+ }
+ // fallback for environments without a `[[Class]]` for `arguments` objects
+ if (!support.argsClass) {
+ isArguments = function(value) {
+ return (value && typeof value == 'object' && typeof value.length == 'number' &&
+ hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee')) || false;
+ };
+ }
+
/**
* Checks if `value` is an array.
*
* @static
* @memberOf _
- * @type Function
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is an array, else `false`.
+ * @returns {boolean} Returns `true` if `value` is an array, else `false`.
* @example
*
- * (function() { return _.isArray(arguments); })();
- * // => false
- *
* _.isArray([1, 2, 3]);
* // => true
+ *
+ * (function() { return _.isArray(arguments); })();
+ * // => false
*/
var isArray = nativeIsArray || function(value) {
- return value && typeof value == 'object' && typeof value.length == 'number' &&
- toString.call(value) == arrayClass || false;
+ return (value && typeof value == 'object' && typeof value.length == 'number' &&
+ toString.call(value) == arrayClass) || false;
};
/**
@@ -5990,32 +6142,38 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a boolean value, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a boolean value, else `false`.
* @example
*
+ * _.isBoolean(false);
+ * // => true
+ *
* _.isBoolean(null);
* // => false
*/
function isBoolean(value) {
- return value === true || value === false ||
- value && typeof value == 'object' && toString.call(value) == boolClass || false;
+ return (value === true || value === false ||
+ value && typeof value == 'object' && toString.call(value) == boolClass) || false;
}
/**
- * Checks if `value` is a date.
+ * Checks if `value` is a `Date` object.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a date, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a date object, else `false`.
* @example
*
* _.isDate(new Date);
* // => true
+ *
+ * _.isDate('Mon April 23 2012');
+ * // => false
*/
function isDate(value) {
- return value && typeof value == 'object' && toString.call(value) == dateClass || false;
+ return (value && typeof value == 'object' && toString.call(value) == dateClass) || false;
}
/**
@@ -6025,56 +6183,63 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a DOM element, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
* @example
*
* _.isElement(document.body);
* // => true
+ *
+ * _.isElement('');
+ * // => false
*/
function isElement(value) {
- return value && typeof value == 'object' && value.nodeType === 1 &&
- (support.nodeClass ? toString.call(value).indexOf('Element') > -1 : isNode(value)) || false;
+ return (value && typeof value == 'object' && value.nodeType === 1 &&
+ (support.nodeClass ? toString.call(value).indexOf('Element') > -1 : isNode(value))) || false;
}
// fallback for environments without DOM support
if (!support.dom) {
isElement = function(value) {
- return value && typeof value == 'object' && value.nodeType === 1 &&
- !isPlainObject(value) || false;
+ return (value && typeof value == 'object' && value.nodeType === 1 &&
+ !isPlainObject(value)) || false;
};
}
/**
- * Checks if `value` is empty. Arrays, strings, or `arguments` objects with a
- * length of `0` and objects with no own enumerable properties are considered
- * "empty".
+ * Checks if a collection is empty. A value is considered empty unless it is
+ * an array, array-like object, or string with a length greater than `0` or
+ * an object with own properties.
*
* @static
* @memberOf _
* @category Objects
* @param {Array|Object|string} value The value to inspect.
- * @returns {boolean} Returns `true` if the `value` is empty, else `false`.
+ * @returns {boolean} Returns `true` if `value` is empty, else `false`.
* @example
*
+ * _.isEmpty(null);
+ * // => true
+ *
+ * _.isEmpty(true);
+ * // => true
+ *
+ * _.isEmpty(1);
+ * // => true
+ *
* _.isEmpty([1, 2, 3]);
* // => false
*
- * _.isEmpty({});
- * // => true
- *
- * _.isEmpty('');
- * // => true
+ * _.isEmpty({ 'a': 1 });
+ * // => false
*/
function isEmpty(value) {
var result = true;
if (!value) {
return result;
}
- var className = toString.call(value),
- length = value.length;
-
- if ((className == arrayClass || className == stringClass ||
- (support.argsClass ? className == argsClass : isArguments(value))) ||
- (className == objectClass && typeof length == 'number' && isFunction(value.splice))) {
+ var length = value.length;
+ if ((length > -1 && length <= maxSafeInteger) &&
+ (isArray(value) || isString(value) || isArguments(value) ||
+ (typeof value == 'object' && isFunction(value.splice)))) {
return !length;
}
baseForOwn(value, function() {
@@ -6085,61 +6250,83 @@
/**
* Performs a deep comparison between two values to determine if they are
- * equivalent to each other. If a callback is provided it will be executed
- * to compare values. If the callback returns `undefined` comparisons will
- * be handled by the method instead. The callback is bound to `thisArg` and
- * invoked with two arguments; (a, b).
+ * equivalent. If a callback is provided it will be executed to compare
+ * values. If the callback returns `undefined` comparisons will be handled
+ * by the method instead. The callback is bound to `thisArg` and invoked
+ * with two arguments; (value, other).
+ *
+ * Note: This method supports comparing arrays, booleans, `Date` objects,
+ * numbers, `Object` objects, regexes, and strings. Functions and DOM nodes
+ * are **not** supported. A callback may be used to extend support for
+ * comparing other values.
*
* @static
* @memberOf _
* @category Objects
- * @param {*} a The value to compare.
- * @param {*} b The other value to compare.
+ * @param {*} value The value to compare to `other`.
+ * @param {*} other The value to compare to `value`.
* @param {Function} [callback] The function to customize comparing values.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
* @example
*
* var object = { 'name': 'fred' };
- * var copy = { 'name': 'fred' };
+ * var other = { 'name': 'fred' };
*
- * object == copy;
+ * object == other;
* // => false
*
- * _.isEqual(object, copy);
+ * _.isEqual(object, other);
* // => true
*
* var words = ['hello', 'goodbye'];
* var otherWords = ['hi', 'goodbye'];
*
- * _.isEqual(words, otherWords, function(a, b) {
- * var reGreet = /^(?:hello|hi)$/i,
- * aGreet = _.isString(a) && reGreet.test(a),
- * bGreet = _.isString(b) && reGreet.test(b);
- *
- * return (aGreet || bGreet) ? (aGreet == bGreet) : undefined;
+ * _.isEqual(words, otherWords, function() {
+ * return _.every(arguments, _.bind(RegExp.prototype.test, /^h(?:i|ello)$/)) || undefined;
* });
* // => true
*/
- function isEqual(a, b, callback, thisArg) {
+ function isEqual(value, other, callback, thisArg) {
callback = typeof callback == 'function' && baseCreateCallback(callback, thisArg, 2);
if (!callback) {
// exit early for identical values
- if (a === b) {
+ if (value === other) {
// treat `-0` vs. `+0` as not equal
- return a !== 0 || (1 / a == 1 / b);
+ return value !== 0 || (1 / value == 1 / other);
}
- var type = typeof a,
- otherType = typeof b;
+ var valType = typeof value,
+ othType = typeof other;
// exit early for unlike primitive values
- if (a === a && (a == null || b == null ||
- (type != 'function' && type != 'object' && otherType != 'function' && otherType != 'object'))) {
+ if (value === value && (value == null || other == null ||
+ (valType != 'function' && valType != 'object' && othType != 'function' && othType != 'object'))) {
return false;
}
}
- return baseIsEqual(a, b, callback);
+ return baseIsEqual(value, other, callback);
+ }
+
+ /**
+ * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`,
+ * `SyntaxError`, `TypeError`, or `URIError` object.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an error object, else `false`.
+ * @example
+ *
+ * _.isError(new Error);
+ * // => true
+ *
+ * _.isError(Error);
+ * // => false
+ */
+ function isError(value) {
+ return (value && typeof value == 'object' && toString.call(value) == errorClass) || false;
}
/**
@@ -6153,7 +6340,7 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is finite, else `false`.
+ * @returns {boolean} Returns `true` if `value` is finite, else `false`.
* @example
*
* _.isFinite(-101);
@@ -6182,11 +6369,14 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a function, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
* @example
*
* _.isFunction(_);
* // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
*/
function isFunction(value) {
return typeof value == 'function';
@@ -6199,14 +6389,14 @@
}
/**
- * Checks if `value` is the language type of Object.
+ * Checks if `value` is the language type of `Object`.
* (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is an object, else `false`.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
* @example
*
* _.isObject({});
@@ -6219,12 +6409,12 @@
* // => false
*/
function isObject(value) {
- // check if the value is the ECMAScript language type of Object
+ // check if the value is the ECMAScript language type of `Object`
// http://es5.github.io/#x8
// and avoid a V8 bug
// https://code.google.com/p/v8/issues/detail?id=2291
var type = typeof value;
- return value && (type == 'function' || type == 'object') || false;
+ return type == 'function' || (value && type == 'object') || false;
}
/**
@@ -6238,7 +6428,7 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is `NaN`, else `false`.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
* @example
*
* _.isNaN(NaN);
@@ -6255,7 +6445,7 @@
*/
function isNaN(value) {
// `NaN` as a primitive is the only value that is not equal to itself
- // (perform the [[Class]] check first to avoid errors with some host objects in IE)
+ // (perform the `[[Class]]` check first to avoid errors with some host objects in IE)
return isNumber(value) && value != +value;
}
@@ -6266,13 +6456,13 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is `null`, else `false`.
+ * @returns {boolean} Returns `true` if `value` is `null`, else `false`.
* @example
*
* _.isNull(null);
* // => true
*
- * _.isNull(undefined);
+ * _.isNull(void 0);
* // => false
*/
function isNull(value) {
@@ -6280,7 +6470,7 @@
}
/**
- * Checks if `value` is a number.
+ * Checks if `value` is a `Number` primitive or object.
*
* Note: `NaN` is considered a number. See the [ES5 spec](http://es5.github.io/#x8.5)
* for more details.
@@ -6289,20 +6479,30 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a number, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a number, else `false`.
* @example
*
- * _.isNumber(8.4 * 5);
+ * _.isNumber(8.4);
* // => true
+ *
+ * _.isNumber(NaN);
+ * // => true
+ *
+ * _.isNumber('8.4');
+ * // => false
*/
function isNumber(value) {
var type = typeof value;
return type == 'number' ||
- value && type == 'object' && toString.call(value) == numberClass || false;
+ (value && type == 'object' && toString.call(value) == numberClass) || false;
}
/**
- * Checks if `value` is an object created by the `Object` constructor.
+ * Checks if `value` is an object created by the `Object` constructor or has
+ * a `[[Prototype]]` of `null`.
+ *
+ * Note: This method assumes objects created by the `Object` constructor
+ * have no inherited enumerable properties.
*
* @static
* @memberOf _
@@ -6324,6 +6524,9 @@
*
* _.isPlainObject({ 'x': 0, 'y': 0 });
* // => true
+ *
+ * _.isPlainObject(Object.create(null));
+ * // => true
*/
var isPlainObject = !getPrototypeOf ? shimIsPlainObject : function(value) {
if (!(value && toString.call(value) == objectClass) || (!support.argsClass && isArguments(value))) {
@@ -6338,40 +6541,44 @@
};
/**
- * Checks if `value` is a regular expression.
+ * Checks if `value` is a `RegExp` object.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a regular expression, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a regexp object, else `false`.
* @example
*
- * _.isRegExp(/fred/);
+ * _.isRegExp(/abc/);
* // => true
+ *
+ * _.isRegExp('/abc/');
+ * // => false
*/
function isRegExp(value) {
- var type = typeof value;
- return value && (type == 'function' || type == 'object') &&
- toString.call(value) == regexpClass || false;
+ return (isObject(value) && toString.call(value) == regexpClass) || false;
}
/**
- * Checks if `value` is a string.
+ * Checks if `value` is a `String` primitive or object.
*
* @static
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is a string, else `false`.
+ * @returns {boolean} Returns `true` if `value` is a string, else `false`.
* @example
*
- * _.isString('fred');
+ * _.isString('abc');
* // => true
+ *
+ * _.isString(1);
+ * // => false
*/
function isString(value) {
return typeof value == 'string' ||
- value && typeof value == 'object' && toString.call(value) == stringClass || false;
+ (value && typeof value == 'object' && toString.call(value) == stringClass) || false;
}
/**
@@ -6381,39 +6588,121 @@
* @memberOf _
* @category Objects
* @param {*} value The value to check.
- * @returns {boolean} Returns `true` if the `value` is `undefined`, else `false`.
+ * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`.
* @example
*
* _.isUndefined(void 0);
* // => true
+ *
+ * _.isUndefined(null);
+ * // => false
*/
function isUndefined(value) {
return typeof value == 'undefined';
}
/**
- * Creates an array composed of the own enumerable property names of an object.
+ * Creates an array of the own enumerable property names of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
- * @returns {Array} Returns an array of property names.
+ * @returns {Array} Returns the array of property names.
* @example
*
- * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
- * // => ['one', 'two', 'three'] (property order is not guaranteed across environments)
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.z = 0;
+ *
+ * _.keys(new Shape);
+ * // => ['x', 'y'] (property order is not guaranteed across environments)
*/
var keys = !nativeKeys ? shimKeys : function(object) {
+ var ctor = object && object.constructor,
+ length = object ? object.length : 0;
+
+ if ((typeof length == 'number' && length > 0) ||
+ (ctor && object === ctor.prototype)) {
+ return shimKeys(object);
+ }
+ return isObject(object) ? nativeKeys(object) : [];
+ };
+
+ /**
+ * Creates an array of the own and inherited enumerable property names of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property names.
+ * @example
+ *
+ * function Shape() {
+ * this.x = 0;
+ * this.y = 0;
+ * }
+ *
+ * Shape.prototype.z = 0;
+ *
+ * _.keysIn(new Shape);
+ * // => ['x', 'y', 'z'] (property order is not guaranteed across environments)
+ */
+ function keysIn(object) {
if (!isObject(object)) {
return [];
}
- if ((support.enumPrototypes && typeof object == 'function') ||
- (support.nonEnumArgs && object.length && isArguments(object))) {
- return shimKeys(object);
+ var length = object.length;
+ length = (typeof length == 'number' && length > 0 &&
+ (isArray(object) || (support.nonEnumStrings && isString(object)) ||
+ (support.nonEnumArgs && isArguments(object))) && length) >>> 0;
+
+ var keyIndex,
+ ctor = object.constructor,
+ index = -1,
+ isProto = ctor && object === ctor.prototype,
+ maxIndex = length - 1,
+ result = Array(length),
+ skipIndexes = length > 0,
+ skipErrorProps = support.enumErrorProps && (object === errorProto || object instanceof Error),
+ skipProto = support.enumPrototypes && typeof object == 'function';
+
+ while (++index < length) {
+ result[index] = String(index);
}
- return nativeKeys(object);
- };
+ for (var key in object) {
+ if (!(isProto && key == 'constructor') &&
+ !(skipProto && key == 'prototype') &&
+ !(skipErrorProps && (key == 'message' || key == 'name')) &&
+ !(skipIndexes && (keyIndex = +key, keyIndex > -1 && keyIndex <= maxIndex && keyIndex % 1 == 0))) {
+ result.push(key);
+ }
+ }
+ // Lo-Dash skips the `constructor` property when it infers it's iterating
+ // over a `prototype` object because IE < 9 can't set the `[[Enumerable]]`
+ // attribute of an existing property and the `constructor` property of a
+ // prototype defaults to non-enumerable.
+ if (support.nonEnumShadows && object !== objectProto) {
+ index = -1;
+ length = shadowedProps.length;
+
+ if (isProto) {
+ var className = object === stringProto ? stringClass : object === errorProto ? errorClass : toString.call(object),
+ nonEnum = nonEnumProps[className];
+ }
+ while (++index < length) {
+ key = shadowedProps[index];
+ if (!(nonEnum && nonEnum[key]) && hasOwnProperty.call(object, key)) {
+ result.push(key);
+ }
+ }
+ }
+ return result;
+ }
/**
* Creates an object with the same keys as `object` and values generated by
@@ -6434,9 +6723,9 @@
* @param {Object} object The object to iterate over.
* @param {Function|Object|string} [callback=identity] The function called
* per iteration. If a property name or object is provided it will be used
- * to create a "_.pluck" or "_.where" style callback, respectively.
+ * to create a "_.pluck" or "_.where" style callback respectively.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Object} Returns a new object with values of the results of each `callback` execution.
+ * @returns {Object} Returns the new mapped object.
* @example
*
* _.mapValues({ 'a': 1, 'b': 2, 'c': 3} , function(num) { return num * 3; });
@@ -6453,8 +6742,8 @@
*/
function mapValues(object, callback, thisArg) {
var result = {};
-
callback = lodash.createCallback(callback, thisArg, 3);
+
baseForOwn(object, function(value, key, object) {
result[key] = callback(value, key, object);
});
@@ -6474,7 +6763,7 @@
* @memberOf _
* @category Objects
* @param {Object} object The destination object.
- * @param {...Object} [source] The source objects.
+ * @param {...Object} [sources] The source objects.
* @param {Function} [callback] The function to customize merging properties.
* @param {*} [thisArg] The `this` binding of `callback`.
* @returns {Object} Returns the destination object.
@@ -6513,13 +6802,13 @@
* // => { 'fruits': ['apple', 'banana'], 'vegetables': ['beet', 'carrot] }
*/
function merge(object, source, guard) {
- if (!isObject(object)) {
- return object;
- }
var args = arguments,
length = args.length,
type = typeof guard;
+ if (!object || length < 2) {
+ return object;
+ }
// enables use as a callback for functions like `_.reduce`
if ((type == 'number' || type == 'string') && args[3] && args[3][guard] === source) {
length = 2;
@@ -6530,7 +6819,7 @@
} else if (length > 2 && typeof args[length - 1] == 'function') {
callback = args[--length];
}
- var sources = slice(arguments, 1, length),
+ var sources = slice(args, 1, length),
index = -1,
stackA = [],
stackB = [];
@@ -6544,20 +6833,20 @@
/**
* Creates a shallow clone of `object` excluding the specified properties.
* Property names may be specified as individual arguments or as arrays of
- * property names. If a callback is provided it will be executed for each
- * property of `object` omitting the properties the callback returns truey
- * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * property names. If a predicate is provided it will be executed for each
+ * property of `object` omitting the properties the predicate returns truthy
+ * for. The predicate is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
- * @param {Function|...string|string[]} [callback] The function called per
+ * @param {Function|...string|string[]} [predicate] The function called per
* iteration or property names to omit, specified as individual property
* names or arrays of property names.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Object} Returns an object without the omitted properties.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Object} Returns the new object.
* @example
*
* _.omit({ 'name': 'fred', 'age': 40 }, 'age');
@@ -6568,49 +6857,29 @@
* });
* // => { 'name': 'fred' }
*/
- function omit(object, callback, thisArg) {
- var result = {};
-
- if (typeof callback != 'function') {
- var omitProps = baseFlatten(arguments, true, false, 1),
- length = omitProps.length;
-
- while (length--) {
- omitProps[length] = String(omitProps[length]);
- }
- var props = [];
- baseForIn(object, function(value, key) {
- props.push(key);
- });
-
- var index = -1;
- props = baseDifference(props, omitProps);
- length = props.length;
-
- while (++index < length) {
- var key = props[index];
- result[key] = object[key];
- }
- } else {
- callback = lodash.createCallback(callback, thisArg, 3);
- baseForIn(object, function(value, key, object) {
- if (!callback(value, key, object)) {
- result[key] = value;
- }
- });
+ function omit(object, predicate, thisArg) {
+ if (typeof predicate == 'function') {
+ predicate = lodash.createCallback(predicate, thisArg, 3);
+ return pick(object, negate(predicate));
}
- return result;
+ var omitProps = baseFlatten(arguments, true, false, 1),
+ length = omitProps.length;
+
+ while (length--) {
+ omitProps[length] = String(omitProps[length]);
+ }
+ return pick(object, baseDifference(keysIn(object), omitProps));
}
/**
- * Creates a two dimensional array of an object's key-value pairs,
+ * Creates a two dimensional array of a given object's key-value pairs,
* i.e. `[[key1, value1], [key2, value2]]`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
- * @returns {Array} Returns new array of key-value pairs.
+ * @returns {Array} Returns the new array of key-value pairs.
* @example
*
* _.pairs({ 'barney': 36, 'fred': 40 });
@@ -6632,20 +6901,20 @@
/**
* Creates a shallow clone of `object` composed of the specified properties.
* Property names may be specified as individual arguments or as arrays of
- * property names. If a callback is provided it will be executed for each
- * property of `object` picking the properties the callback returns truey
- * for. The callback is bound to `thisArg` and invoked with three arguments;
+ * property names. If a predicate is provided it will be executed for each
+ * property of `object` picking the properties the predicate returns truthy
+ * for. The predicate is bound to `thisArg` and invoked with three arguments;
* (value, key, object).
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The source object.
- * @param {Function|...string|string[]} [callback] The function called per
+ * @param {Function|...string|string[]} [predicate] The function called per
* iteration or property names to pick, specified as individual property
* names or arrays of property names.
- * @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Object} Returns an object composed of the picked properties.
+ * @param {*} [thisArg] The `this` binding of `predicate`.
+ * @returns {Object} Returns the new object.
* @example
*
* _.pick({ 'name': 'fred', '_userid': 'fred1' }, 'name');
@@ -6656,10 +6925,10 @@
* });
* // => { 'name': 'fred' }
*/
- function pick(object, callback, thisArg) {
+ function pick(object, predicate, thisArg) {
var result = {};
- if (typeof callback != 'function') {
+ if (typeof predicate != 'function') {
var index = -1,
props = baseFlatten(arguments, true, false, 1),
length = isObject(object) ? props.length : 0;
@@ -6671,9 +6940,9 @@
}
}
} else {
- callback = lodash.createCallback(callback, thisArg, 3);
+ predicate = lodash.createCallback(predicate, thisArg, 3);
baseForIn(object, function(value, key, object) {
- if (callback(value, key, object)) {
+ if (predicate(value, key, object)) {
result[key] = value;
}
});
@@ -6718,15 +6987,16 @@
if (isArr) {
accumulator = [];
} else {
- var ctor = object && object.constructor,
- proto = ctor && ctor.prototype;
-
+ if (isObject(object)) {
+ var ctor = object.constructor,
+ proto = ctor && ctor.prototype;
+ }
accumulator = baseCreate(proto);
}
}
if (callback) {
callback = lodash.createCallback(callback, thisArg, 4);
- (isArr ? baseEach : baseForOwn)(object, function(value, index, object) {
+ (isArr ? arrayEach : baseForOwn)(object, function(value, index, object) {
return callback(accumulator, value, index, object);
});
}
@@ -6734,39 +7004,90 @@
}
/**
- * Creates an array composed of the own enumerable property values of `object`.
+ * Creates an array of the own enumerable property values of `object`.
*
* @static
* @memberOf _
* @category Objects
* @param {Object} object The object to inspect.
- * @returns {Array} Returns an array of property values.
+ * @returns {Array} Returns the array of property values.
* @example
*
- * _.values({ 'one': 1, 'two': 2, 'three': 3 });
- * // => [1, 2, 3] (property order is not guaranteed across environments)
+ * function Shape(x, y) {
+ * this.x = x;
+ * this.y = y;
+ * }
+ *
+ * Shape.prototype.z = 0;
+ *
+ * _.values(new Shape(2, 1));
+ * // => [2, 1] (property order is not guaranteed across environments)
*/
function values(object) {
- var index = -1,
- props = keys(object),
- length = props.length,
- result = Array(length);
+ return baseValues(object, keys);
+ }
- while (++index < length) {
- result[index] = object[props[index]];
- }
- return result;
+ /**
+ * Creates an array of the own and inherited enumerable property values
+ * of `object`.
+ *
+ * @static
+ * @memberOf _
+ * @category Objects
+ * @param {Object} object The object to inspect.
+ * @returns {Array} Returns the array of property values.
+ * @example
+ *
+ * function Shape(x, y) {
+ * this.x = x;
+ * this.y = y;
+ * }
+ *
+ * Shape.prototype.z = 0;
+ *
+ * _.valuesIn(new Shape(2, 1));
+ * // => [2, 1, 0] (property order is not guaranteed across environments)
+ */
+ function valuesIn(object) {
+ return baseValues(object, keysIn);
}
/*--------------------------------------------------------------------------*/
/**
- * Converts the first character of `string` to upper case.
+ * Converts `string` to camel case.
+ * See [Wikipedia](http://en.wikipedia.org/wiki/CamelCase) for more details.
*
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to capitalize.
+ * @param {string} [string=''] The string to camel case.
+ * @returns {string} Returns the camel cased string.
+ * @example
+ *
+ * _.camelCase('Hello world');
+ * // => 'helloWorld'
+ *
+ * _.camelCase('--hello-world');
+ * // => 'helloWorld'
+ *
+ * _.camelCase('__hello_world__');
+ * // => 'helloWorld'
+ */
+ var camelCase = createCompounder(function(result, word, index) {
+ if (!index && reAllCaps.test(word)) {
+ return result + word.toLowerCase();
+ }
+ return result + (word.charAt(0)[index ? 'toUpperCase' : 'toLowerCase']() + word.slice(1));
+ });
+
+ /**
+ * Capitalizes the first character of `string`.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to capitalize.
* @returns {string} Returns the capitalized string.
* @example
*
@@ -6781,6 +7102,37 @@
return string.charAt(0).toUpperCase() + string.slice(1);
}
+ /**
+ * Checks if `string` ends with a given target string.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to search.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=string.length] The position to search from.
+ * @returns {boolean} Returns `true` if the given string ends with the
+ * target string, else `false`.
+ * @example
+ *
+ * _.endsWith('abc', 'c');
+ * // => true
+ *
+ * _.endsWith('abc', 'b');
+ * // => false
+ *
+ * _.endsWith('abc', 'b', 2);
+ * // => true
+ */
+ function endsWith(string, target, position) {
+ string = string == null ? '' : String(string);
+ target = String(target);
+
+ var length = string.length;
+ position = (typeof position == 'undefined' ? length : nativeMin(position < 0 ? 0 : (+position || 0), length)) - target.length;
+ return position >= 0 && string.indexOf(target, position) == position;
+ }
+
/**
* Converts the characters "&", "<", ">", '"', and "'" in `string` to
* their corresponding HTML entities.
@@ -6795,7 +7147,7 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to escape.
+ * @param {string} [string=''] The string to escape.
* @returns {string} Returns the escaped string.
* @example
*
@@ -6806,6 +7158,234 @@
return string == null ? '' : String(string).replace(reUnescapedHtml, escapeHtmlChar);
}
+ /**
+ * Escapes the `RegExp` special characters "\", "^", "$", ".", "|", "?", "*",
+ * "+", "(", ")", "[", "]", "{" and "}" in `string`.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to escape.
+ * @returns {string} Returns the escaped string.
+ * @example
+ *
+ * _.escapeRegExp('[lodash](http://lodash.com)');
+ * // => '\[lodash\]\(http://lodash\.com\)'
+ */
+ function escapeRegExp(string) {
+ return string == null ? '' : String(string).replace(reRegExpChars, '\\$&');
+ }
+
+ /**
+ * Converts `string` to kebab case (a.k.a. spinal case).
+ * See [Wikipedia](http://en.wikipedia.org/wiki/Letter_case#Computers) for
+ * more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to kebab case.
+ * @returns {string} Returns the kebab cased string.
+ * @example
+ *
+ * _.kebabCase('Hello world');
+ * // => 'hello-world'
+ *
+ * _.kebabCase('helloWorld');
+ * // => 'hello-world'
+ *
+ * _.kebabCase('__hello_world__');
+ * // => 'hello-world'
+ */
+ var kebabCase = createCompounder(function(result, word, index) {
+ return result + (index ? '-' : '') + word.toLowerCase();
+ });
+
+ /**
+ * Pads `string` on the left and right sides if it is shorter then the given
+ * padding length. The `chars` string may be truncated if the number of padding
+ * characters can't be evenly divided by the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.pad('abc', 8);
+ * // => ' abc '
+ *
+ * _.pad('abc', 8, '_-');
+ * // => '_-abc_-_'
+ *
+ * _.pad('abc', 3);
+ * // => 'abc'
+ */
+ function pad(string, length, chars) {
+ string = string == null ? '' : String(string);
+ length = +length;
+
+ var strLength = string.length;
+ if (strLength >= length || !nativeIsFinite(length)) {
+ return string;
+ }
+ var mid = (length - strLength) / 2,
+ leftLength = floor(mid),
+ rightLength = ceil(mid);
+
+ chars = createPad('', rightLength, chars);
+ return chars.slice(0, leftLength) + string + chars;
+ }
+
+ /**
+ * Pads `string` on the left side if it is shorter then the given padding
+ * length. The `chars` string may be truncated if the number of padding
+ * characters exceeds the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.padLeft('abc', 6);
+ * // => ' abc'
+ *
+ * _.padLeft('abc', 6, '_-');
+ * // => '_-_abc'
+ *
+ * _.padLeft('abc', 3);
+ * // => 'abc'
+ */
+ function padLeft(string, length, chars) {
+ string = string == null ? '' : String(string);
+ return createPad(string, length, chars) + string;
+ }
+
+ /**
+ * Pads `string` on the right side if it is shorter then the given padding
+ * length. The `chars` string may be truncated if the number of padding
+ * characters exceeds the padding length.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to pad.
+ * @param {number} [length=0] The padding length.
+ * @param {string} [chars=' '] The string used as padding.
+ * @returns {string} Returns the padded string.
+ * @example
+ *
+ * _.padRight('abc', 6);
+ * // => 'abc '
+ *
+ * _.padRight('abc', 6, '_-');
+ * // => 'abc_-_'
+ *
+ * _.padRight('abc', 3);
+ * // => 'abc'
+ */
+ function padRight(string, length, chars) {
+ string = string == null ? '' : String(string);
+ return string + createPad(string, length, chars);
+ }
+
+ /**
+ * Repeats the given string `n` times.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to repeat.
+ * @param {number} [n=0] The number of times to repeat the string.
+ * @returns {string} Returns the repeated string.
+ * @example
+ *
+ * _.repeat('*', 3);
+ * // => '***'
+ *
+ * _.repeat('abc', 2);
+ * // => 'abcabc'
+ *
+ * _.repeat('abc', 0);
+ * // => ''
+ */
+ function repeat(string, n) {
+ var result = '';
+ n = +n;
+
+ if (n < 1 || string == null || !nativeIsFinite(n)) {
+ return result;
+ }
+ string = String(string);
+ do {
+ if (n % 2) {
+ result += string;
+ }
+ n = floor(n / 2);
+ string += string;
+ } while (n);
+ return result;
+ }
+
+ /**
+ * Converts `string` to snake case.
+ * See [Wikipedia](http://en.wikipedia.org/wiki/Snake_case) for more details.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to snake case.
+ * @returns {string} Returns the snake cased string.
+ * @example
+ *
+ * _.snakeCase('Hello world');
+ * // => 'hello_world'
+ *
+ * _.snakeCase('--hello-world');
+ * // => 'hello_world'
+ *
+ * _.snakeCase('helloWorld');
+ * // => 'hello_world'
+ */
+ var snakeCase = createCompounder(function(result, word, index) {
+ return result + (index ? '_' : '') + word.toLowerCase();
+ });
+
+ /**
+ * Checks if `string` starts with a given target string.
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to search.
+ * @param {string} [target] The string to search for.
+ * @param {number} [position=0] The position to search from.
+ * @returns {boolean} Returns `true` if the given string starts with the
+ * target string, else `false`.
+ * @example
+ *
+ * _.startsWith('abc', 'a');
+ * // => true
+ *
+ * _.startsWith('abc', 'b');
+ * // => false
+ *
+ * _.startsWith('abc', 'b', 1);
+ * // => true
+ */
+ function startsWith(string, target, position) {
+ string = string == null ? '' : String(string);
+ position = typeof position == 'undefined' ? 0 : nativeMin(position < 0 ? 0 : (+position || 0), string.length);
+ return string.lastIndexOf(target, position) == position;
+ }
+
/**
* Creates a compiled template function that can interpolate data properties
* in "interpolate" delimiters, HTML-escaped interpolated data properties in
@@ -6815,8 +7395,8 @@
* settings object is provided it will override `_.templateSettings` for the
* template.
*
- * Note: In the development build, `_.template` utilizes sourceURLs for easier
- * debugging. See [HTML5 Rocks' article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
+ * Note: In the development build, `_.template` utilizes `sourceURL`s for easier debugging.
+ * See the [HTML5 Rocks article on sourcemaps](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl)
* for more details.
*
* For more information on precompiling templates see
@@ -6828,17 +7408,17 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} text The template text.
- * @param {Object} [data] The data object used to populate the text.
+ * @param {string} [string=''] The template string.
+ * @param {Object} [data] The data object used to populate the template string.
* @param {Object} [options] The options object.
* @param {RegExp} [options.escape] The HTML "escape" delimiter.
* @param {RegExp} [options.evaluate] The "evaluate" delimiter.
* @param {Object} [options.imports] An object to import into the template as local variables.
* @param {RegExp} [options.interpolate] The "interpolate" delimiter.
- * @param {string} [options.sourceURL] The sourceURL of the template's compiled source.
+ * @param {string} [options.sourceURL] The `sourceURL` of the template's compiled source.
* @param {string} [options.variable] The data object variable name.
* @returns {Function|string} Returns the interpolated string if a data object
- * is provided, else it returns a template function.
+ * is provided, else the compiled template function.
* @example
*
* // using the "interpolate" delimiter to create a compiled template
@@ -6873,7 +7453,7 @@
* _.template(list, { 'people': ['fred', 'barney'] }, { 'imports': { 'jq': jQuery } });
* // => 'fredbarney'
*
- * // using the `sourceURL` option to specify a custom sourceURL for the template
+ * // using the `sourceURL` option to specify a custom `sourceURL` for the template
* var compiled = _.template('hello <%= name %>', null, { 'sourceURL': '/basic/greeting.jst' });
* compiled(data);
* // => find the source of "greeting.jst" under the Sources tab or Resources panel of the web inspector
@@ -6895,20 +7475,18 @@
* };\
* ');
*/
- function template(text, data, options) {
+ function template(string, data, options) {
// based on John Resig's `tmpl` implementation
// http://ejohn.org/blog/javascript-micro-templating/
// and Laura Doktorova's doT.js
// https://github.com/olado/doT
var settings = lodash.templateSettings;
- text = String(text || '');
+ options = defaults({}, options, settings);
+ string = String(string == null ? '' : string);
- // avoid missing dependencies when `iteratorTemplate` is not defined
- options = iteratorTemplate ? defaults({}, options, settings) : settings;
-
- var imports = iteratorTemplate && defaults({}, options.imports, settings.imports),
- importsKeys = iteratorTemplate ? keys(imports) : ['_'],
- importsValues = iteratorTemplate ? values(imports) : [lodash];
+ var imports = defaults({}, options.imports, settings.imports),
+ importsKeys = keys(imports),
+ importsValues = values(imports);
var isEscaping,
isEvaluating,
@@ -6924,11 +7502,11 @@
(options.evaluate || reNoMatch).source + '|$'
, 'g');
- text.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
+ string.replace(reDelimiters, function(match, escapeValue, interpolateValue, esTemplateValue, evaluateValue, offset) {
interpolateValue || (interpolateValue = esTemplateValue);
// escape characters that cannot be included in string literals
- source += text.slice(index, offset).replace(reUnescapedString, escapeStringChar);
+ source += string.slice(index, offset).replace(reUnescapedString, escapeStringChar);
// replace delimiters with snippets
if (escapeValue) {
@@ -6981,7 +7559,7 @@
source +
'return __p\n}';
- // Use a sourceURL for easier debugging.
+ // Use a `sourceURL` for easier debugging.
// http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl
var sourceURL = '\n/*\n//# sourceURL=' + (options.sourceURL || '/lodash/template/source[' + (templateCounter++) + ']') + '\n*/';
@@ -7007,7 +7585,7 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to trim.
+ * @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
@@ -7018,12 +7596,17 @@
* _.trim('-_-fred-_-', '_-');
* // => 'fred'
*/
- var trim = !nativeTrim ? shimTrim : function(string, chars) {
- if (string == null) {
- return '';
+ function trim(string, chars) {
+ string = string == null ? '' : String(string);
+ if (!string) {
+ return string;
}
- return chars == null ? nativeTrim.call(string) : shimTrim(string, chars);
- };
+ if (chars == null) {
+ return string.slice(trimmedLeftIndex(string), trimmedRightIndex(string) + 1);
+ }
+ chars = String(chars);
+ return string.slice(charsLeftIndex(string, chars), charsRightIndex(string, chars) + 1);
+ }
/**
* Removes leading whitespace or specified characters from `string`.
@@ -7031,7 +7614,7 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to trim.
+ * @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
@@ -7042,12 +7625,17 @@
* _.trimLeft('-_-fred-_-', '_-');
* // => 'fred-_-'
*/
- var trimLeft = !nativeTrimLeft ? shimTrimLeft : function(string, chars) {
- if (string == null) {
- return '';
+ function trimLeft(string, chars) {
+ string = string == null ? '' : String(string);
+ if (!string) {
+ return string;
}
- return chars == null ? nativeTrimLeft.call(string) : shimTrimLeft(string, chars);
- };
+ if (chars == null) {
+ return string.slice(trimmedLeftIndex(string))
+ }
+ chars = String(chars);
+ return string.slice(charsLeftIndex(string, chars));
+ }
/**
* Removes trailing whitespace or specified characters from `string`.
@@ -7055,7 +7643,7 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to trim.
+ * @param {string} [string=''] The string to trim.
* @param {string} [chars=whitespace] The characters to trim.
* @returns {string} Returns the trimmed string.
* @example
@@ -7066,12 +7654,96 @@
* _.trimRight('-_-fred-_-', '_-');
* // => '-_-fred'
*/
- var trimRight = !nativeTrimRight ? shimTrimRight : function(string, chars) {
- if (string == null) {
- return '';
+ function trimRight(string, chars) {
+ string = string == null ? '' : String(string);
+ if (!string) {
+ return string;
}
- return chars == null ? nativeTrimRight.call(string) : shimTrimRight(string, chars);
- };
+ if (chars == null) {
+ return string.slice(0, trimmedRightIndex(string) + 1)
+ }
+ chars = String(chars);
+ return string.slice(0, charsRightIndex(string, chars) + 1);
+ }
+
+ /**
+ * Truncates `string` if it is longer than the given maximum string length.
+ * The last characters of the truncated string will be replaced with the
+ * omission string which defaults to "...".
+ *
+ * @static
+ * @memberOf _
+ * @category Strings
+ * @param {string} [string=''] The string to truncate.
+ * @param {Object|number} [options] The options object or maximum string length.
+ * @param {number} [options.length=30] The maximum string length.
+ * @param {string} [options.omission='...'] The string used to indicate text is omitted.
+ * @param {RegExp|string} [options.separator] The separator pattern to truncate to.
+ * @returns {string} Returns the truncated string.
+ * @example
+ *
+ * _.truncate('hi-diddly-ho there, neighborino');
+ * // => 'hi-diddly-ho there, neighbo...'
+ *
+ * _.truncate('hi-diddly-ho there, neighborino', 24);
+ * // => 'hi-diddly-ho there, n...'
+ *
+ * _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': ' ' });
+ * // => 'hi-diddly-ho there,...'
+ *
+ * _.truncate('hi-diddly-ho there, neighborino', { 'length': 24, 'separator': /,? +/ });
+ * //=> 'hi-diddly-ho there...'
+ *
+ * _.truncate('hi-diddly-ho there, neighborino', { 'omission': ' [...]' });
+ * // => 'hi-diddly-ho there, neig [...]'
+ */
+ function truncate(string, options) {
+ var length = 30,
+ omission = '...';
+
+ if (options && isObject(options)) {
+ var separator = 'separator' in options ? options.separator : separator;
+ length = 'length' in options ? +options.length || 0 : length;
+ omission = 'omission' in options ? String(options.omission) : omission;
+ }
+ else if (options != null) {
+ length = +options || 0;
+ }
+ string = string == null ? '' : String(string);
+ if (length >= string.length) {
+ return string;
+ }
+ var end = length - omission.length;
+ if (end < 1) {
+ return omission;
+ }
+ var result = string.slice(0, end);
+ if (separator == null) {
+ return result + omission;
+ }
+ if (isRegExp(separator)) {
+ if (string.slice(end).search(separator)) {
+ var match,
+ newEnd,
+ substring = string.slice(0, end);
+
+ if (!separator.global) {
+ separator = RegExp(separator.source, (reFlags.exec(separator) || '') + 'g');
+ }
+ separator.lastIndex = 0;
+ while ((match = separator.exec(substring))) {
+ newEnd = match.index;
+ }
+ result = result.slice(0, newEnd == null ? end : newEnd);
+ }
+ } else if (string.indexOf(separator, end) != end) {
+ var index = result.lastIndexOf(separator);
+ if (index > -1) {
+ result = result.slice(0, index);
+ }
+ }
+ return result + omission;
+ }
/**
* The inverse of `_.escape`; this method converts the HTML entities
@@ -7084,7 +7756,7 @@
* @static
* @memberOf _
* @category Strings
- * @param {string} string The string to unescape.
+ * @param {string} [string=''] The string to unescape.
* @returns {string} Returns the unescaped string.
* @example
*
@@ -7123,7 +7795,7 @@
}
/**
- * Produces a callback bound to an optional `thisArg`. If `func` is a property
+ * Creates a function bound to an optional `thisArg`. If `func` is a property
* name the created callback will return the property value for a given element.
* If `func` is an object the created callback will return `true` for elements
* that contain the equivalent object properties, otherwise it will return `false`.
@@ -7135,7 +7807,7 @@
* @param {*} [func=identity] The value to convert to a callback.
* @param {*} [thisArg] The `this` binding of the created callback.
* @param {number} [argCount] The number of arguments the callback accepts.
- * @returns {Function} Returns a callback function.
+ * @returns {Function} Returns the new function.
* @example
*
* var characters = [
@@ -7157,11 +7829,11 @@
function createCallback(func, thisArg, argCount) {
var type = typeof func;
if (type == 'function' || func == null) {
- return (typeof thisArg == 'undefined' || !('prototype' in func)) &&
+ return (typeof thisArg == 'undefined' || !(func && 'prototype' in func)) &&
func || baseCreateCallback(func, thisArg, argCount);
}
// handle "_.pluck" and "_.where" style callback shorthands
- return type != 'object' ? property(func) : matches(func);
+ return type == 'object' ? matches(func) : property(func);
}
/**
@@ -7183,9 +7855,9 @@
}
/**
- * Creates a "_.where" style function, which performs a deep comparison
- * between a given object and the `source` object, returning `true` if the
- * given object has equivalent property values, else `false`.
+ * Creates a "_.where" style predicate function which performs a deep comparison
+ * between a given object and the `source` object, returning `true` if the given
+ * object has equivalent property values, else `false`.
*
* @static
* @memberOf _
@@ -7208,32 +7880,33 @@
* // => { 'name': 'barney', 'age': 36 }
*/
function matches(source) {
- source || (source = {});
-
var props = keys(source),
+ propsLength = props.length,
key = props[0],
- a = source[key];
+ value = propsLength && source[key];
// fast path the common case of providing an object with a single
// property containing a primitive value
- if (props.length == 1 && a === a && !isObject(a)) {
+ if (propsLength == 1 && value === value && !isObject(value)) {
return function(object) {
- if (!hasOwnProperty.call(object, key)) {
+ if (!(object && hasOwnProperty.call(object, key))) {
return false;
}
// treat `-0` vs. `+0` as not equal
- var b = object[key];
- return a === b && (a !== 0 || (1 / a == 1 / b));
+ var other = object[key];
+ return value === other && (value !== 0 || (1 / value == 1 / other));
};
}
return function(object) {
- var length = props.length,
- result = false;
-
+ var length = propsLength;
+ if (length && !object) {
+ return false;
+ }
+ var result = true;
while (length--) {
var key = props[length];
if (!(result = hasOwnProperty.call(object, key) &&
- baseIsEqual(object[key], source[key], null, true))) {
+ baseIsEqual(object[key], source[key], null, true))) {
break;
}
}
@@ -7248,10 +7921,12 @@
* @static
* @memberOf _
* @category Utilities
- * @param {Function|Object} [object=lodash] object The destination object.
+ * @param {Function|Object} [object=this] object The destination object.
* @param {Object} source The object of functions to add.
* @param {Object} [options] The options object.
- * @param {boolean} [options.chain=true] Specify whether the functions added are chainable.
+ * @param {boolean} [options.chain=true] Specify whether the functions added
+ * are chainable.
+ * @returns {Function|Object} Returns `object`.
* @example
*
* function vowels(string) {
@@ -7280,7 +7955,7 @@
options = source;
}
source = object;
- object = lodash;
+ object = this;
methodNames = functions(source);
}
if (options === false) {
@@ -7317,10 +7992,11 @@
}(func));
}
}
+ return object;
}
/**
- * Reverts the '_' variable to its previous value and returns a reference to
+ * Reverts the `_` variable to its previous value and returns a reference to
* the `lodash` function.
*
* @static
@@ -7371,8 +8047,8 @@
/**
* Converts `value` to an integer of the specified radix. If `radix` is
- * `undefined` or `0` a `radix` of `10` is used unless the `value` is a
- * hexadecimal, in which case a `radix` of `16` is used.
+ * `undefined` or `0`, a `radix` of `10` is used unless `value` is a hexadecimal,
+ * in which case a `radix` of `16` is used.
*
* Note: This method avoids differences in native ES3 and ES5 `parseInt`
* implementations. See the [ES5 spec](http://es5.github.io/#E)
@@ -7383,7 +8059,7 @@
* @category Utilities
* @param {string} value The value to parse.
* @param {number} [radix] The radix used to interpret the value to parse.
- * @returns {number} Returns the new integer value.
+ * @returns {number} Returns the converted integer.
* @example
*
* _.parseInt('08');
@@ -7398,7 +8074,7 @@
};
/**
- * Creates a "_.pluck" style function, which returns the `key` value of a
+ * Creates a "_.pluck" style function which returns the `key` value of a
* given object.
*
* @static
@@ -7423,14 +8099,14 @@
*/
function property(key) {
return function(object) {
- return object[key];
+ return object == null ? undefined : object[key];
};
}
/**
* Produces a random number between `min` and `max` (inclusive). If only one
* argument is provided a number between `0` and the given number will be
- * returned. If `floating` is truey or either `min` or `max` are floats a
+ * returned. If `floating` is truthy or either `min` or `max` are floats a
* floating-point number will be returned instead of an integer.
*
* @static
@@ -7439,7 +8115,7 @@
* @param {number} [min=0] The minimum possible value.
* @param {number} [max=1] The maximum possible value.
* @param {boolean} [floating=false] Specify returning a floating-point number.
- * @returns {number} Returns a random number.
+ * @returns {number} Returns the random number.
* @example
*
* _.random(0, 5);
@@ -7486,6 +8162,61 @@
return baseRandom(min, max);
}
+ /**
+ * Creates an array of numbers (positive and/or negative) progressing from
+ * `start` up to but not including `end`. If `start` is less than `stop` a
+ * zero-length range is created unless a negative `step` is specified.
+ *
+ * @static
+ * @memberOf _
+ * @category Utilities
+ * @param {number} [start=0] The start of the range.
+ * @param {number} end The end of the range.
+ * @param {number} [step=1] The value to increment or decrement by.
+ * @returns {Array} Returns the new array of numbers.
+ * @example
+ *
+ * _.range(4);
+ * // => [0, 1, 2, 3]
+ *
+ * _.range(1, 5);
+ * // => [1, 2, 3, 4]
+ *
+ * _.range(0, 20, 5);
+ * // => [0, 5, 10, 15]
+ *
+ * _.range(0, -4, -1);
+ * // => [0, -1, -2, -3]
+ *
+ * _.range(1, 4, 0);
+ * // => [1, 1, 1]
+ *
+ * _.range(0);
+ * // => []
+ */
+ function range(start, end, step) {
+ start = +start || 0;
+ step = step == null ? 1 : (+step || 0);
+
+ if (end == null) {
+ end = start;
+ start = 0;
+ } else {
+ end = +end || 0;
+ }
+ // use `Array(length)` so engines like Chakra and V8 avoid slower modes
+ // http://youtu.be/XAqIpGU8ZZk#t=17m25s
+ var index = -1,
+ length = nativeMax(ceil((end - start) / (step || 1)), 0),
+ result = Array(length);
+
+ while (++index < length) {
+ result[index] = start;
+ start += step;
+ }
+ return result;
+ }
+
/**
* Resolves the value of property `key` on `object`. If `key` is a function
* it will be invoked with the `this` binding of `object` and its result
@@ -7536,9 +8267,9 @@
* @memberOf _
* @category Utilities
* @param {number} n The number of times to execute the callback.
- * @param {Function} callback The function called per iteration.
+ * @param {Function} [callback=identity] The function called per iteration.
* @param {*} [thisArg] The `this` binding of `callback`.
- * @returns {Array} Returns an array of the results of each `callback` execution.
+ * @returns {Array} Returns the array of results.
* @example
*
* var diceRolls = _.times(3, _.partial(_.random, 1, 6));
@@ -7551,11 +8282,12 @@
* // => also calls `mage.castSpell(n)` three times
*/
function times(n, callback, thisArg) {
- n = (n = +n) > -1 ? n : 0;
+ n = n < 0 ? 0 : n >>> 0;
+ callback = baseCreateCallback(callback, thisArg, 1);
+
var index = -1,
result = Array(n);
- callback = baseCreateCallback(callback, thisArg, 1);
while (++index < n) {
result[index] = callback(index);
}
@@ -7605,6 +8337,10 @@
lodash.defer = defer;
lodash.delay = delay;
lodash.difference = difference;
+ lodash.drop = drop;
+ lodash.dropRight = dropRight;
+ lodash.dropRightWhile = dropRightWhile;
+ lodash.dropWhile = dropWhile;
lodash.filter = filter;
lodash.flatten = flatten;
lodash.forEach = forEach;
@@ -7621,6 +8357,7 @@
lodash.invert = invert;
lodash.invoke = invoke;
lodash.keys = keys;
+ lodash.keysIn = keysIn;
lodash.map = map;
lodash.mapValues = mapValues;
lodash.matches = matches;
@@ -7628,6 +8365,8 @@
lodash.memoize = memoize;
lodash.merge = merge;
lodash.min = min;
+ lodash.mixin = mixin;
+ lodash.negate = negate;
lodash.omit = omit;
lodash.once = once;
lodash.pairs = pairs;
@@ -7638,10 +8377,10 @@
lodash.pluck = pluck;
lodash.property = property;
lodash.pull = pull;
+ lodash.pullAt = pullAt;
lodash.range = range;
lodash.reject = reject;
lodash.remove = remove;
- lodash.removeAt = removeAt;
lodash.rest = rest;
lodash.shuffle = shuffle;
lodash.slice = slice;
@@ -7654,6 +8393,7 @@
lodash.union = union;
lodash.uniq = uniq;
lodash.values = values;
+ lodash.valuesIn = valuesIn;
lodash.where = where;
lodash.without = without;
lodash.wrap = wrap;
@@ -7664,7 +8404,6 @@
// add aliases
lodash.callback = createCallback;
lodash.collect = map;
- lodash.drop = rest;
lodash.each = forEach;
lodash.eachRight = forEachRight;
lodash.extend = assign;
@@ -7676,16 +8415,19 @@
lodash.unzip = zip;
// add functions to `lodash.prototype`
- mixin(assign({}, lodash));
+ mixin(lodash, assign({}, lodash));
/*--------------------------------------------------------------------------*/
// add functions that return unwrapped values when chaining
+ lodash.camelCase = camelCase;
lodash.capitalize = capitalize;
lodash.clone = clone;
lodash.cloneDeep = cloneDeep;
lodash.contains = contains;
+ lodash.endsWith = endsWith;
lodash.escape = escape;
+ lodash.escapeRegExp = escapeRegExp;
lodash.every = every;
lodash.find = find;
lodash.findIndex = findIndex;
@@ -7693,6 +8435,7 @@
lodash.findLast = findLast;
lodash.findLastIndex = findLastIndex;
lodash.findLastKey = findLastKey;
+ lodash.findWhere = findWhere;
lodash.has = has;
lodash.identity = identity;
lodash.indexOf = indexOf;
@@ -7703,6 +8446,7 @@
lodash.isElement = isElement;
lodash.isEmpty = isEmpty;
lodash.isEqual = isEqual;
+ lodash.isError = isError;
lodash.isFinite = isFinite;
lodash.isFunction = isFunction;
lodash.isNaN = isNaN;
@@ -7713,24 +8457,31 @@
lodash.isRegExp = isRegExp;
lodash.isString = isString;
lodash.isUndefined = isUndefined;
+ lodash.kebabCase = kebabCase;
lodash.lastIndexOf = lastIndexOf;
- lodash.mixin = mixin;
lodash.noConflict = noConflict;
lodash.noop = noop;
lodash.now = now;
+ lodash.pad = pad;
+ lodash.padLeft = padLeft;
+ lodash.padRight = padRight;
lodash.parseInt = parseInt;
lodash.random = random;
lodash.reduce = reduce;
lodash.reduceRight = reduceRight;
+ lodash.repeat = repeat;
lodash.result = result;
lodash.runInContext = runInContext;
lodash.size = size;
lodash.some = some;
lodash.sortedIndex = sortedIndex;
+ lodash.snakeCase = snakeCase;
+ lodash.startsWith = startsWith;
lodash.template = template;
lodash.trim = trim;
lodash.trimLeft = trimLeft;
lodash.trimRight = trimRight;
+ lodash.truncate = truncate;
lodash.unescape = unescape;
lodash.uniqueId = uniqueId;
@@ -7738,13 +8489,12 @@
lodash.all = every;
lodash.any = some;
lodash.detect = find;
- lodash.findWhere = find;
lodash.foldl = reduce;
lodash.foldr = reduceRight;
lodash.include = contains;
lodash.inject = reduce;
- mixin(function() {
+ mixin(lodash, function() {
var source = {}
baseForOwn(lodash, function(func, methodName) {
if (!lodash.prototype[methodName]) {
@@ -7760,9 +8510,12 @@
lodash.first = first;
lodash.last = last;
lodash.sample = sample;
+ lodash.take = take;
+ lodash.takeRight = takeRight;
+ lodash.takeRightWhile = takeRightWhile;
+ lodash.takeWhile = takeWhile;
// add aliases
- lodash.take = first;
lodash.head = first;
baseForOwn(lodash, function(func, methodName) {
@@ -7792,12 +8545,13 @@
// add "Chaining" functions to the wrapper
lodash.prototype.chain = wrapperChain;
+ lodash.prototype.toJSON = wrapperValueOf;
lodash.prototype.toString = wrapperToString;
lodash.prototype.value = wrapperValueOf;
lodash.prototype.valueOf = wrapperValueOf;
// add `Array` functions that return unwrapped values
- baseEach(['join', 'pop', 'shift'], function(methodName) {
+ arrayEach(['join', 'pop', 'shift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
var chainAll = this.__chain__,
@@ -7810,7 +8564,7 @@
});
// add `Array` functions that return the existing wrapped value
- baseEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
+ arrayEach(['push', 'reverse', 'sort', 'unshift'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
func.apply(this.__wrapped__, arguments);
@@ -7819,7 +8573,7 @@
});
// add `Array` functions that return new wrapped values
- baseEach(['concat', 'splice'], function(methodName) {
+ arrayEach(['concat', 'splice'], function(methodName) {
var func = arrayRef[methodName];
lodash.prototype[methodName] = function() {
return new lodashWrapper(func.apply(this.__wrapped__, arguments), this.__chain__);
@@ -7829,7 +8583,7 @@
// avoid array-like object bugs with `Array#shift` and `Array#splice`
// in IE < 9, Firefox < 10, Narwhal, and RingoJS
if (!support.spliceObjects) {
- baseEach(['pop', 'shift', 'splice'], function(methodName) {
+ arrayEach(['pop', 'shift', 'splice'], function(methodName) {
var func = arrayRef[methodName],
isSplice = methodName == 'splice';
@@ -7847,12 +8601,6 @@
};
});
}
-
- // add pseudo private property to be used and removed during the build process
- lodash._baseForIn = baseForIn;
- lodash._iteratorTemplate = iteratorTemplate;
- lodash._shimKeys = shimKeys;
-
return lodash;
}
diff --git a/test/test.js b/test/test.js
index 69e153d3e..f60bd7a10 100644
--- a/test/test.js
+++ b/test/test.js
@@ -4,7 +4,10 @@
var undefined;
/** Used as the size to cover large array optimizations */
- var LARGE_ARRAY_SIZE = 200;
+ var largeArraySize = 200;
+
+ /** Used as the maximum length an array-like object */
+ var maxSafeInteger = Math.pow(2, 53) - 1;
/** Used as a reference to the global object */
var root = typeof global == 'object' && global || this;
@@ -25,7 +28,9 @@
push = Array.prototype.push,
slice = Array.prototype.slice,
system = root.system,
- toString = Object.prototype.toString,
+ toString = Object.prototype.toString;
+
+ var JSON = root.JSON,
Worker = document && root.Worker;
/** The file path of the Lo-Dash file to test */
@@ -78,8 +83,8 @@
/** Detect if testing `npm` modules */
var isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]);
- /** Detects if running in a PhantomJS web page */
- var isPhantomPage = typeof callPhantom == 'function';
+ /** Detects if running in PhantomJS */
+ var isPhantom = phantom || typeof callPhantom == 'function';
/** Detect if running in Rhino */
var isRhino = isJava && typeof global == 'function' && global().Array === root.Array;
@@ -180,7 +185,7 @@
return result;
}());
- /** Used to check problem JScript properties (a.k.a. the [[DontEnum]] bug) */
+ /** Used to check problem JScript properties (a.k.a. the `[[DontEnum]]` bug) */
var shadowedProps = [
'constructor',
'hasOwnProperty',
@@ -262,6 +267,7 @@
"'_array': [1, 2, 3],",
"'_boolean': new Boolean(false),",
"'_date': new Date,",
+ "'_errors': [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],",
"'_function': function() {},",
"'_nan': NaN,",
"'_null': null,",
@@ -269,7 +275,7 @@
"'_object': { 'a': 1, 'b': 2, 'c': 3 },",
"'_regexp': /x/,",
"'_string': new String('a'),",
- "'_undefined': undefined,",
+ "'_undefined': undefined",
'})'
].join('\n')));
}
@@ -288,15 +294,12 @@
}
// allow bypassing native checks
var _fnToString = Function.prototype.toString;
- setProperty(Function.prototype, 'toString', (function() {
- function fnToString() {
- setProperty(Function.prototype, 'toString', _fnToString);
- var result = this === Set ? this.toString() : _fnToString.call(this);
- setProperty(Function.prototype, 'toString', fnToString);
- return result;
- }
- return fnToString;
- }()));
+ setProperty(Function.prototype, 'toString', function wrapper() {
+ setProperty(Function.prototype, 'toString', _fnToString);
+ var result = this === Set ? this.toString() : _fnToString.call(this);
+ setProperty(Function.prototype, 'toString', wrapper);
+ return result;
+ });
// fake DOM
setProperty(global, 'window', {});
@@ -318,7 +321,7 @@
var _now = Date.now;
setProperty(Date, 'now', function() {});
- var _create = Object.create;
+ var _create = create;
setProperty(Object, 'create', function() {});
var _defineProperty = Object.defineProperty;
@@ -330,18 +333,17 @@
var _keys = Object.keys;
setProperty(Object, 'keys', function() {});
+ var _hasOwnProperty = Object.prototype.hasOwnProperty;
+ setProperty(Object.prototype, 'hasOwnProperty', function(key) {
+ if (key == '1' && _.isArguments(this) && _.isEqual(_.values(this), [0, 0])) {
+ throw new Error;
+ }
+ return _hasOwnProperty.call(this, key);
+ });
+
var _contains = String.prototype.contains;
setProperty(String.prototype, 'contains', _contains ? function() {} : Boolean);
- var _trim = String.prototype.trim;
- setProperty(String.prototype, 'trim', _trim ? function() {} : String);
-
- var _trimLeft = String.prototype.trimLeft;
- setProperty(String.prototype, 'trimLeft', _trimLeft ? function() {} : String);
-
- var _trimRight = String.prototype.trimRight;
- setProperty(String.prototype, 'trimRight', _trimRight ? function() {} : String);
-
// clear cache so Lo-Dash can be reloaded
emptyObject(require.cache);
@@ -355,22 +357,14 @@
setProperty(Object, 'defineProperty', _defineProperty);
setProperty(Object, 'getPrototypeOf', _getPrototypeOf);
setProperty(Object, 'keys', _keys);
+ setProperty(Object.prototype, 'hasOwnProperty', _hasOwnProperty);
setProperty(Function.prototype, 'toString', _fnToString);
- _.forOwn({
- 'contains': _contains,
- 'trim': _trim,
- 'trimLeft': _trimLeft,
- 'trimRight': _trimRight
- },
- function(func, key) {
- if (func) {
- setProperty(String.prototype, key, func);
- } else {
- delete String.prototype[key];
- }
- });
-
+ if (_contains) {
+ setProperty(String.prototype, 'contains', _contains);
+ } else {
+ delete String.prototype.contains;
+ }
delete global.window;
delete global.WinRTError;
delete Function.prototype._method;
@@ -393,6 +387,7 @@
'parent._._boolean = new Boolean(false);',
'parent._._date = new Date;',
"parent._._element = document.createElement('div');",
+ 'parent._._errors = [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError];',
'parent._._function = function() {};',
'parent._._nan = NaN;',
'parent._._null = null;',
@@ -428,7 +423,7 @@
(function() {
test('supports loading ' + basename + ' as the "lodash" module', 1, function() {
if (amd) {
- equal((lodashModule || {}).moduleName, 'lodash');
+ strictEqual((lodashModule || {}).moduleName, 'lodash');
}
else {
skipTest();
@@ -437,7 +432,7 @@
test('supports loading ' + basename + ' with the Require.js "shim" configuration option', 1, function() {
if (amd && /requirejs/.test(ui.loaderPath)) {
- equal((shimmedModule || {}).moduleName, 'shimmed');
+ strictEqual((shimmedModule || {}).moduleName, 'shimmed');
} else {
skipTest();
}
@@ -445,7 +440,7 @@
test('supports loading ' + basename + ' as the "underscore" module', 1, function() {
if (amd && !/dojo/.test(ui.loaderPath)) {
- equal((underscoreModule || {}).moduleName, 'underscore');
+ strictEqual((underscoreModule || {}).moduleName, 'underscore');
}
else {
skipTest();
@@ -463,7 +458,7 @@
setTimeout(attempt, 16);
return;
}
- equal(actual, _.VERSION);
+ strictEqual(actual, _.VERSION);
QUnit.start();
};
@@ -477,28 +472,27 @@
test('should not add `Function.prototype` extensions to lodash', 1, function() {
if (lodashBizarro) {
- equal('_method' in lodashBizarro, false);
+ ok(!('_method' in lodashBizarro));
}
else {
skipTest();
}
});
- test('should avoid overwritten native methods', 12, function() {
+ test('should avoid overwritten native methods', 9, function() {
function Foo() {}
function message(methodName) {
return '`_.' + methodName + '` should avoid overwritten native methods';
}
- var object = { 'a': true };
- var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
- return object;
- });
+ var object = { 'a': 1 },
+ otherObject = { 'b': 2 },
+ largeArray = _.times(largeArraySize, _.constant(object));
if (lodashBizarro) {
try {
- actual = [lodashBizarro.isArray([]), lodashBizarro.isArray({ 'length': 0 })];
+ var actual = [lodashBizarro.isArray([]), lodashBizarro.isArray({ 'length': 0 })];
} catch(e) {
actual = null;
}
@@ -520,11 +514,11 @@
deepEqual(actual[1], {}, message('Object.create'));
try {
- var actual = lodashBizarro.bind(function() { return this.a; }, object);
+ actual = lodashBizarro.bind(function() { return this.a; }, object);
} catch(e) {
actual = null;
}
- equal(expando in actual, false, message('Object.defineProperty'));
+ ok(!(expando in actual), message('Object.defineProperty'));
try {
actual = [lodashBizarro.isPlainObject({}), lodashBizarro.isPlainObject([])];
@@ -542,14 +536,14 @@
try {
actual = [
- lodashBizarro.difference([object], largeArray),
+ lodashBizarro.difference([object, otherObject], largeArray),
lodashBizarro.intersection(largeArray, [object]),
lodashBizarro.uniq(largeArray)
];
} catch(e) {
actual = null;
}
- deepEqual(actual, [[], [object], [object]], message('Set'));
+ deepEqual(actual, [[otherObject], [object], [object]], message('Set'));
try {
actual = lodashBizarro.contains('abc', 'c');
@@ -557,23 +551,9 @@
actual = null;
}
strictEqual(actual, true, message('String#contains'));
-
- _.forEach(['trim', 'trimLeft', 'trimRight'], function(methodName) {
- try {
- var actual = [
- lodashBizarro[methodName](whitespace + 'a b c' + whitespace),
- lodashBizarro[methodName](''),
- lodashBizarro[methodName]('-_-a-b-c-_-', '_-'),
- lodashBizarro[methodName]('', '_-')
- ];
- } catch(e) {
- actual = null;
- }
- ok(_.every(actual, _.isString), message('String#' + methodName));
- });
}
else {
- skipTest(12);
+ skipTest(9);
}
});
}());
@@ -589,7 +569,7 @@
test('should return provided `lodash` instances', 1,function() {
var wrapped = _(false);
- equal(_(wrapped), wrapped);
+ strictEqual(_(wrapped), wrapped);
});
}());
@@ -598,18 +578,23 @@
QUnit.module('lodash.after');
(function() {
+ function after(n, times) {
+ var count = 0;
+ _.times(times, _.after(n, function() { count++; }));
+ return count;
+ }
test('should create a function that executes `func` after `n` calls', 4, function() {
- function after(n, times) {
- var count = 0;
- _.times(times, _.after(n, function() { count++; }));
- return count;
- }
-
strictEqual(after(5, 5), 1, 'after(n) should execute `func` after being called `n` times');
strictEqual(after(5, 4), 0, 'after(n) should not execute `func` unless called `n` times');
strictEqual(after(0, 0), 0, 'after(0) should not execute `func` immediately');
strictEqual(after(0, 1), 1, 'after(0) should execute `func` when called once');
});
+
+ test('should coerce non-finite `n` values to `0`', 3, function() {
+ _.each([-Infinity, NaN, Infinity], function(n) {
+ strictEqual(after(n, 1), 1);
+ });
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -626,7 +611,6 @@
this.a = 1;
this.c = 3;
}
-
Foo.prototype.b = 2;
deepEqual(_.assign({}, new Foo), { 'a': 1, 'c': 3 });
});
@@ -647,14 +631,6 @@
deepEqual(_.assign({ 'a': 1, 'b': 2 }, expected), expected);
});
- test('should not error on `null` or `undefined` sources (test in IE < 9)', 1, function() {
- try {
- deepEqual(_.assign({}, null, undefined, { 'a': 1 }), { 'a': 1 });
- } catch(e) {
- ok(false);
- }
- });
-
test('should work with a callback', 1, function() {
var actual = _.assign({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) {
return typeof a == 'undefined' ? b : a;
@@ -694,8 +670,8 @@
var args = arguments;
test('should return `undefined` for nonexistent keys', 1, function() {
- var actual = _.at(['a', 'b', 'c'], [0, 2, 4]);
- deepEqual(actual, ['a', 'c', undefined]);
+ var actual = _.at(['a', 'b', 'c'], [2, 4, 0]);
+ deepEqual(actual, ['c', undefined, 'a']);
});
test('should return an empty array when no keys are provided', 1, function() {
@@ -703,34 +679,27 @@
});
test('should accept multiple key arguments', 1, function() {
- var actual = _.at(['a', 'b', 'c', 'd'], 0, 2, 3);
- deepEqual(actual, ['a', 'c', 'd']);
+ var actual = _.at(['a', 'b', 'c', 'd'], 3, 0, 2);
+ deepEqual(actual, ['d', 'a', 'c']);
});
test('should work with an `arguments` object for `collection`', 1, function() {
- var actual = _.at(args, [0, 2]);
- deepEqual(actual, ['a', 'c']);
+ var actual = _.at(args, [2, 0]);
+ deepEqual(actual, ['c', 'a']);
});
test('should work with an object for `collection`', 1, function() {
- var actual = _.at({ 'a': 1, 'b': 2, 'c': 3 }, ['a', 'c']);
- deepEqual(actual, [1, 3]);
+ var actual = _.at({ 'a': 1, 'b': 2, 'c': 3 }, ['c', 'a']);
+ deepEqual(actual, [3, 1]);
});
- test('should work when used as a callback for `_.map`', 1, function() {
- var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
- actual = _.map(array, _.at);
-
- deepEqual(actual, [[1], [5], [9]]);
- });
-
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
function(collection, key) {
test('should work with a string ' + key + ' for `collection`', 1, function() {
- deepEqual(_.at(collection, [0, 2]), ['a', 'c']);
+ deepEqual(_.at(collection, [2, 0]), ['c', 'a']);
});
});
}('a', 'b', 'c'));
@@ -757,7 +726,7 @@
var values = _.reject(falsey.slice(1), function(value) { return value == null; }),
expected = _.map(values, function(value) { return [value]; });
- var actual = _.map(values, function(value, index) {
+ var actual = _.map(values, function(value) {
try {
var bound = _.bind(fn, value);
return bound();
@@ -774,14 +743,14 @@
actual = bound('a');
ok(actual[0] === null || actual[0] && actual[0].Array);
- equal(actual[1], 'a');
+ strictEqual(actual[1], 'a');
_.times(2, function(index) {
bound = index ? _.bind(fn, undefined) : _.bind(fn);
actual = bound('b');
ok(actual[0] === undefined || actual[0] && actual[0].Array);
- equal(actual[1], 'b');
+ strictEqual(actual[1], 'b');
});
});
@@ -800,7 +769,7 @@
});
test('should support placeholders', 4, function() {
- if (_._iteratorTemplate) {
+ if (!isModularize) {
var object = {},
bound = _.bind(fn, object, _, 'b', _);
@@ -815,7 +784,7 @@
});
test('should create a function with a `length` of `0`', 2, function() {
- var func = function(a, b, c) {},
+ var fn = function(a, b, c) {},
bound = _.bind(fn, {});
strictEqual(bound.length, 0);
@@ -828,6 +797,7 @@
function Foo() {
return this;
}
+
var bound = _.bind(Foo, { 'a': 1 }),
newBound = new bound;
@@ -840,6 +810,7 @@
function Foo(value) {
return value && object;
}
+
var bound = _.bind(Foo),
object = {};
@@ -854,10 +825,6 @@
deepEqual(bound(['b'], 'c'), [object, 'a', ['b'], 'c']);
});
- test('should throw a TypeError if `func` is not a function', 1, function() {
- raises(function() { _.bind(); }, TypeError);
- });
-
test('should return a wrapped value when chaining', 2, function() {
if (!isNpm) {
var object = {},
@@ -901,7 +868,6 @@
this._b = 2;
this.a = function() { return this._a; };
}
-
Foo.prototype.b = function() { return this._b; };
var object = new Foo;
@@ -985,17 +951,122 @@
var object = {
'name': 'fred',
'greet': function(greeting) {
- return greeting + ' ' + this.name;
+ return this.name + ' says: ' + greeting;
}
};
- var func = _.bindKey(object, 'greet', 'hi');
- equal(func(), 'hi fred');
+ var bound = _.bindKey(object, 'greet', 'hi');
+ strictEqual(bound(), 'fred says: hi');
object.greet = function(greeting) {
- return greeting + ' ' + this.name + '!';
+ return this.name + ' says: ' + greeting + '!';
};
- equal(func(), 'hi fred!');
+ strictEqual(bound(), 'fred says: hi!');
+ });
+
+ test('should support placeholders', 4, function() {
+ var object = {
+ 'fn': function fn(a, b, c, d) {
+ return slice.call(arguments);
+ }
+ };
+
+ if (!isModularize) {
+ var bound = _.bindKey(object, 'fn', _, 'b', _);
+ deepEqual(bound('a', 'c'), ['a', 'b', 'c']);
+ deepEqual(bound('a'), ['a', 'b', undefined]);
+ deepEqual(bound('a', 'c', 'd'), ['a', 'b', 'c', 'd']);
+ deepEqual(bound(), [undefined, 'b', undefined]);
+ }
+ else {
+ skipTest(4);
+ }
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('case methods');
+
+ _.each(['camel', 'kebab', 'snake'], function(caseName) {
+ var methodName = caseName + 'Case',
+ func = _[methodName];
+
+ var expected = (function() {
+ switch (caseName) {
+ case 'camel': return 'helloWorld';
+ case 'kebab': return 'hello-world';
+ case 'snake': return 'hello_world';
+ }
+ }());
+
+ var burredLetters = [
+ '\xC0', '\xC1', '\xC2', '\xC3', '\xC4', '\xC5', '\xC6', '\xC7', '\xC8', '\xC9', '\xCA', '\xCB', '\xCC', '\xCD', '\xCE', '\xCF',
+ '\xD0', '\xD1', '\xD2', '\xD3', '\xD4', '\xD5', '\xD6', '\xD7', '\xD8', '\xD9', '\xDA', '\xDB', '\xDC', '\xDD', '\xDE', '\xDF',
+ '\xE0', '\xE1', '\xE2', '\xE3', '\xE4', '\xE5', '\xE6', '\xE7', '\xE8', '\xE9', '\xEA', '\xEB', '\xEC', '\xED', '\xEE', '\xEF',
+ '\xF0', '\xF1', '\xF2', '\xF3', '\xF4', '\xF5', '\xF6', '\xF7', '\xF8', '\xF9', '\xFA', '\xFB', '\xFC', '\xFD', '\xFE', '\xFF'
+ ];
+
+ var deburredLetters = [
+ 'A', 'A', 'A', 'A', 'A', 'A', 'AE', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', 'I',
+ 'D', 'N', 'O', 'O', 'O', 'O', 'O', '', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th', 'ss',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', 'i',
+ 'd', 'n', 'o', 'o', 'o', 'o', 'o', '', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y'
+ ];
+
+ test('`_.' + methodName + '` should convert `string` to ' + caseName + ' case', 4, function() {
+ _.each(['Hello world', 'helloWorld', '--hello-world', '__hello_world__'], function(string) {
+ strictEqual(func(string), expected);
+ });
+ });
+
+ test('`_.' + methodName + '` should handle double-converting strings', 4, function() {
+ _.each(['Hello world', 'helloWorld', '--hello-world', '__hello_world__'], function(string) {
+ strictEqual(func(func(string)), expected);
+ });
+ });
+
+ test('`_.' + methodName + '` should deburr letters', 1, function() {
+ var actual = _.map(burredLetters, function(burred, index) {
+ var isCamel = caseName == 'camel',
+ deburrLetter = deburredLetters[index];
+
+ var string = isCamel
+ ? func('z' + burred)
+ : func(burred);
+
+ var deburredString = isCamel
+ ? 'z' + deburrLetter
+ : deburrLetter.toLowerCase();
+
+ return string == deburredString;
+ });
+
+ ok(_.every(actual, _.identity));
+ });
+
+ test('`_.' + methodName + '` should coerce `string` to a string', 2, function() {
+ var string = 'Hello world';
+ strictEqual(func(Object(string)), expected);
+ strictEqual(func({ 'toString': _.constant(string) }), expected);
+ });
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.camelCase');
+
+ (function() {
+ test('should work with numbers', 3, function() {
+ strictEqual(_.camelCase('too legit 2 quit'), 'tooLegit2Quit');
+ strictEqual(_.camelCase('walk 500 miles'), 'walk500Miles');
+ strictEqual(_.camelCase('xhr2 request'), 'xhr2Request');
+ });
+
+ test('should handle acronyms', 3, function() {
+ strictEqual(_.camelCase('safe HTML'), 'safeHTML');
+ strictEqual(_.camelCase('escape HTML entities'), 'escapeHTMLEntities');
+ strictEqual(_.camelCase('XMLHttpRequest'), 'xmlHttpRequest');
});
}());
@@ -1005,15 +1076,9 @@
(function() {
test('should capitalize the first character of a string', 3, function() {
- equal(_.capitalize('fred'), 'Fred');
- equal(_.capitalize('Fred'), 'Fred');
- equal(_.capitalize(' fred'), ' fred');
- });
-
- test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() {
- strictEqual(_.capitalize(null), '');
- strictEqual(_.capitalize(undefined), '');
- strictEqual(_.capitalize(''), '');
+ strictEqual(_.capitalize('fred'), 'Fred');
+ strictEqual(_.capitalize('Fred'), 'Fred');
+ strictEqual(_.capitalize(' fred'), ' fred');
});
}());
@@ -1035,7 +1100,7 @@
test('should return the existing wrapper when chaining', 1, function() {
if (!isNpm) {
var wrapper = _({ 'a': 0 });
- equal(wrapper.chain(), wrapper);
+ strictEqual(wrapper.chain(), wrapper);
}
else {
skipTest();
@@ -1119,7 +1184,7 @@
Klass.prototype = { 'b': 1 };
var nonCloneable = {
- 'an element': body,
+ 'a DOM element': body,
'a function': Klass
};
@@ -1149,14 +1214,14 @@
actual = _.clone(expected);
deepEqual(actual, expected);
- ok(actual != expected && actual[0] === expected[0]);
+ ok(actual !== expected && actual[0] === expected[0]);
});
test('`_.clone` should perform a shallow clone when used as a callback for `_.map`', 1, function() {
var expected = [{ 'a': [0] }, { 'b': [1] }],
actual = _.map(expected, _.clone);
- ok(actual[0] != expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b);
+ ok(actual[0] !== expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b);
});
test('`_.cloneDeep` should deep clone objects with circular references', 1, function() {
@@ -1172,7 +1237,7 @@
ok(clone.bar.b === clone.foo.b && clone === clone.foo.b.foo.c && clone !== object);
});
- _.forEach([
+ _.each([
'clone',
'cloneDeep'
],
@@ -1219,7 +1284,7 @@
return this[value];
}, { 'a': 'A' });
- equal(actual, 'A');
+ strictEqual(actual, 'A');
});
test('`_.' + methodName + '` should handle cloning if `callback` returns `undefined`', 1, function() {
@@ -1232,7 +1297,7 @@
actual = func(array);
strictEqual(actual.index, 2);
- equal(actual.input, 'vwxyz');
+ strictEqual(actual.input, 'vwxyz');
});
test('`_.' + methodName + '` should deep clone `lastIndex` regexp property', 1, function() {
@@ -1241,7 +1306,21 @@
regexp.exec('vwxyz');
var actual = func(regexp);
- equal(actual.lastIndex, 3);
+ strictEqual(actual.lastIndex, 3);
+ });
+
+ test('`_.' + methodName + '` should not error on DOM elements', 1, function() {
+ if (document) {
+ var element = document.createElement('div');
+ try {
+ strictEqual(func(element), element);
+ } catch(e) {
+ ok(false);
+ }
+ }
+ else {
+ skipTest();
+ }
});
});
}(1, 2, 3));
@@ -1277,7 +1356,7 @@
};
var welcome = _.compose(greet, format);
- equal(welcome('pebbles'), 'Hiya Penelope!');
+ strictEqual(welcome('pebbles'), 'Hiya Penelope!');
});
test('should return a new function', 1, function() {
@@ -1327,40 +1406,59 @@
QUnit.module('lodash.contains');
(function() {
- _.forEach({
+ _.each({
'an `arguments` object': arguments,
- 'an array': [1, 2, 3, 1, 2, 3],
- 'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 1, 'e': 2, 'f': 3 },
- 'a string': '123123'
+ 'an array': [1, 2, 3, 4],
+ 'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 4 },
+ 'a string': '1234'
},
function(collection, key) {
+ var values = _.toArray(collection);
+
test('should work with ' + key + ' and return `true` for matched values', 1, function() {
strictEqual(_.contains(collection, 3), true);
});
test('should work with ' + key + ' and return `false` for unmatched values', 1, function() {
- strictEqual(_.contains(collection, 4), false);
+ strictEqual(_.contains(collection, 5), false);
});
- test('should work with ' + key + ' and a positive `fromIndex`', 1, function() {
- strictEqual(_.contains(collection, 1, 2), true);
+ test('should work with ' + key + ' and a positive `fromIndex`', 2, function() {
+ strictEqual(_.contains(collection, values[2], 2), true);
+ strictEqual(_.contains(collection, values[1], 2), false);
});
- test('should work with ' + key + ' and a `fromIndex` >= `collection.length`', 6, function() {
- _.forEach([6, 8], function(fromIndex) {
+ test('should work with ' + key + ' and a `fromIndex` >= `collection.length`', 12, function() {
+ _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
strictEqual(_.contains(collection, 1, fromIndex), false);
strictEqual(_.contains(collection, undefined, fromIndex), false);
strictEqual(_.contains(collection, '', fromIndex), false);
});
});
- test('should work with ' + key + ' and a negative `fromIndex`', 1, function() {
- strictEqual(_.contains(collection, 2, -3), true);
+ test('should work with ' + key + ' and treat falsey `fromIndex` values as `0`', 1, function() {
+ var expected = _.map(falsey, _.constant(true));
+
+ var actual = _.map(falsey, function(fromIndex) {
+ return _.contains(collection, values[0], fromIndex);
+ });
+
+ deepEqual(actual, expected);
});
- test('should work with ' + key + ' and a negative `fromIndex` <= negative `collection.length`', 2, function() {
- strictEqual(_.contains(collection, 1, -6), true);
- strictEqual(_.contains(collection, 2, -8), true);
+ test('should work with ' + key + ' and treat non-number `fromIndex` values as `0`', 1, function() {
+ strictEqual(_.contains(collection, values[0], '1'), true);
+ });
+
+ test('should work with ' + key + ' and a negative `fromIndex`', 2, function() {
+ strictEqual(_.contains(collection, values[2], -2), true);
+ strictEqual(_.contains(collection, values[1], -2), false);
+ });
+
+ test('should work with ' + key + ' and a negative `fromIndex` <= negative `collection.length`', 3, function() {
+ _.each([-4, -6, -Infinity], function(fromIndex) {
+ strictEqual(_.contains(collection, values[0], fromIndex), true);
+ });
});
test('should work with ' + key + ' and return an unwrapped value when chaining', 1, function() {
@@ -1373,7 +1471,7 @@
});
});
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -1391,7 +1489,7 @@
test('should be aliased', 1, function() {
strictEqual(_.include, _.contains);
});
- }(1, 2, 3, 1, 2, 3));
+ }(1, 2, 3, 4));
/*--------------------------------------------------------------------------*/
@@ -1522,7 +1620,7 @@
test('should ignore primitive `prototype` arguments and use an empty object instead', 1, function() {
var primitives = [true, null, 1, 'a', undefined],
- expected = _.map(primitives, function() { return true; });
+ expected = _.map(primitives, _.constant(true));
var actual = _.map(primitives, function(value, index) {
return _.isPlainObject(index ? _.create(value) : _.create());
@@ -1537,28 +1635,60 @@
QUnit.module('lodash.callback');
(function() {
- test('should work with functions created by `_.partial` and `_.partialRight`', 2, function() {
- var fn = function() {
- var result = [this.a];
- push.apply(result, arguments);
- return result;
- };
+ test('should create a callback with a falsey `thisArg`', 1, function() {
+ var values = _.map(falsey, function(value) {
+ return Object(value == null ? root : value);
+ });
- var expected = [1, 2, 3],
- object = { 'a': 1 },
- callback = _.createCallback(_.partial(fn, 2), object);
+ var actual = _.map(values, function(value) {
+ var callback = _.callback(function() { return this; }, value);
+ return callback();
+ });
- deepEqual(callback(3), expected);
+ deepEqual(actual, values);
+ });
- callback = _.createCallback(_.partialRight(fn, 3), object);
- deepEqual(callback(2), expected);
+ test('should return `_.identity` when `func` is nullish', 2, function() {
+ var object = {};
+ _.each([null, undefined], function(value) {
+ var callback = _.callback(value);
+ strictEqual(callback(object), object);
+ });
+ });
+
+ test('should not error when `func` is nullish and a `thisArg` is provided', 2, function() {
+ var object = {};
+ _.each([null, undefined], function(value) {
+ try {
+ var callback = _.callback(value, {});
+ strictEqual(callback(object), object);
+ } catch(e) {
+ ok(false);
+ }
+ });
+ });
+
+ test('should return a callback created by `_.matches` when `func` is an object', 2, function() {
+ var callback = _.callback({ 'a': 1 });
+ strictEqual(callback({ 'a': 1, 'b': 2 }), true);
+ strictEqual(callback({}), false);
+ });
+
+ test('should return a callback created by `_.property` when `func` is a number or string', 2, function() {
+ var array = ['a'],
+ callback = _.callback(0);
+
+ strictEqual(callback(array), 'a');
+
+ callback = _.callback('0');
+ strictEqual(callback(array), 'a');
});
test('should work without an `argCount`', 1, function() {
var args,
expected = ['a', 'b', 'c', 'd', 'e'];
- var callback = _.createCallback(function() {
+ var callback = _.callback(function() {
args = slice.call(arguments);
});
@@ -1566,6 +1696,23 @@
deepEqual(args, expected);
});
+ test('should work with functions created by `_.partial` and `_.partialRight`', 2, function() {
+ function fn() {
+ var result = [this.a];
+ push.apply(result, arguments);
+ return result;
+ }
+
+ var expected = [1, 2, 3],
+ object = { 'a': 1 },
+ callback = _.callback(_.partial(fn, 2), object);
+
+ deepEqual(callback(3), expected);
+
+ callback = _.callback(_.partialRight(fn, 3), object);
+ deepEqual(callback(2), expected);
+ });
+
test('should return the function provided if already bound with `Function#bind`', 1, function() {
function a() {}
@@ -1574,7 +1721,7 @@
if (bound && !('prototype' in bound)) {
var bound = a.bind(object);
- strictEqual(_.createCallback(bound, object), bound);
+ strictEqual(_.callback(bound, object), bound);
}
else {
skipTest();
@@ -1588,8 +1735,8 @@
var object = {};
if (_.support.funcDecomp) {
- strictEqual(_.createCallback(a, object), a);
- notStrictEqual(_.createCallback(b, object), b);
+ strictEqual(_.callback(a, object), a);
+ notStrictEqual(_.callback(b, object), b);
}
else {
skipTest(2);
@@ -1598,21 +1745,21 @@
test('should only write metadata to named functions', 3, function() {
function a() {};
+ var b = function() {};
function c() {};
- var b = function() {},
- object = {};
+ var object = {};
if (defineProperty && _.support.funcDecomp) {
- _.createCallback(a, object);
+ _.callback(a, object);
ok(expando in a);
- _.createCallback(b, object);
- equal(expando in b, false);
+ _.callback(b, object);
+ ok(!(expando in b));
if (_.support.funcNames) {
_.support.funcNames = false;
- _.createCallback(c, object);
+ _.callback(c, object);
ok(expando in c);
_.support.funcNames = true;
@@ -1630,8 +1777,8 @@
function a() {};
if (defineProperty && lodashBizarro) {
- lodashBizarro.createCallback(a, {});
- equal(expando in a, false);
+ lodashBizarro.callback(a, {});
+ ok(!(expando in a));
}
else {
skipTest();
@@ -1645,25 +1792,42 @@
(function() {
function fn(a, b, c, d) {
- return a + b + c + d;
+ return slice.call(arguments);
}
test('should curry based on the number of arguments provided', 3, function() {
- var curried = _.curry(fn);
- equal(curried(1)(2)(3)(4), 10);
- equal(curried(1, 2)(3, 4), 10);
- equal(curried(1, 2, 3, 4), 10);
+ var curried = _.curry(fn),
+ expected = [1, 2, 3, 4];
+
+ deepEqual(curried(1)(2)(3)(4), expected);
+ deepEqual(curried(1, 2)(3, 4), expected);
+ deepEqual(curried(1, 2, 3, 4), expected);
});
test('should work with partialed methods', 2, function() {
var curried = _.curry(fn),
- a = _.partial(curried, 1),
+ expected = [1, 2, 3, 4];
+
+ var a = _.partial(curried, 1),
b = _.bind(a, null, 2),
c = _.partialRight(b, 4),
d = _.partialRight(b(3), 4);
- equal(c(3), 10);
- equal(d(), 10);
+ deepEqual(c(3), expected);
+ deepEqual(d(), expected);
+ });
+
+ test('should support placeholders', 4, function() {
+ if (!isModularize) {
+ var curried = _.curry(fn);
+ deepEqual(curried(1)(_, 3)(_, 4)(2), [1, 2, 3, 4]);
+ deepEqual(curried(_, 2)(1)(_, 4)(3), [1, 2, 3, 4]);
+ deepEqual(curried(_, _, 3)(_, 2)(_, 4)(1), [1, 2, 3, 4]);
+ deepEqual(curried(_, _, _, 4)(_, _, 3)(_, 2)(1), [1, 2, 3, 4]);
+ }
+ else {
+ skipTest(4);
+ }
});
test('should return a function with a `length` of `0`', 6, function() {
@@ -1679,6 +1843,7 @@
function Foo(value) {
return value && object;
}
+
var curried = _.curry(Foo),
object = {};
@@ -1687,24 +1852,26 @@
});
test('should not alter the `this` binding', 9, function() {
- var fn = function(a, b, c) {
+ function fn(a, b, c) {
var value = this || {};
- return value[a] + value[b] + value[c];
- };
+ return [value[a], value[b], value[c]];
+ }
- var object = { 'a': 1, 'b': 2, 'c': 3 };
- equal(_.curry(_.bind(fn, object), 3)('a')('b')('c'), 6);
- equal(_.curry(_.bind(fn, object), 3)('a', 'b')('c'), 6);
- equal(_.curry(_.bind(fn, object), 3)('a', 'b', 'c'), 6);
+ var object = { 'a': 1, 'b': 2, 'c': 3 },
+ expected = [1, 2, 3];
- ok(_.isEqual(_.bind(_.curry(fn), object)('a')('b')('c'), NaN));
- ok(_.isEqual(_.bind(_.curry(fn), object)('a', 'b')('c'), NaN));
- equal(_.bind(_.curry(fn), object)('a', 'b', 'c'), 6);
+ deepEqual(_.curry(_.bind(fn, object), 3)('a')('b')('c'), expected);
+ deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b')('c'), expected);
+ deepEqual(_.curry(_.bind(fn, object), 3)('a', 'b', 'c'), expected);
+
+ deepEqual(_.bind(_.curry(fn), object)('a')('b')('c'), Array(3));
+ deepEqual(_.bind(_.curry(fn), object)('a', 'b')('c'), Array(3));
+ deepEqual(_.bind(_.curry(fn), object)('a', 'b', 'c'), expected);
object.curried = _.curry(fn);
- ok(_.isEqual(object.curried('a')('b')('c'), NaN));
- ok(_.isEqual(object.curried('a', 'b')('c'), NaN));
- equal(object.curried('a', 'b', 'c'), 6);
+ deepEqual(object.curried('a')('b')('c'), Array(3));
+ deepEqual(object.curried('a', 'b')('c'), Array(3));
+ deepEqual(object.curried('a', 'b', 'c'), expected);
});
}());
@@ -1722,10 +1889,10 @@
debounced();
debounced();
- equal(count, 0);
+ strictEqual(count, 0);
setTimeout(function() {
- equal(count, 1);
+ strictEqual(count, 1);
QUnit.start();
}, 96);
}
@@ -1796,12 +1963,12 @@
}
});
- asyncTest('should work with `leading` option', 7, function() {
+ asyncTest('should support a `leading` option', 7, function() {
if (!(isRhino && isModularize)) {
var withLeading,
counts = [0, 0, 0];
- _.forEach([true, { 'leading': true }], function(options, index) {
+ _.each([true, { 'leading': true }], function(options, index) {
var debounced = _.debounce(function(value) {
counts[index]++;
return value;
@@ -1810,10 +1977,10 @@
if (index == 1) {
withLeading = debounced;
}
- equal(debounced('x'), 'x');
+ strictEqual(debounced('x'), 'x');
});
- _.forEach([false, { 'leading': false }], function(options) {
+ _.each([false, { 'leading': false }], function(options) {
var withoutLeading = _.debounce(_.identity, 32, options);
strictEqual(withoutLeading('x'), undefined);
});
@@ -1831,7 +1998,7 @@
deepEqual(counts, [1, 1, 2]);
withLeading('x');
- equal(counts[1], 2);
+ strictEqual(counts[1], 2);
QUnit.start();
}, 64);
@@ -1842,7 +2009,7 @@
}
});
- asyncTest('should work with `trailing` option', 4, function() {
+ asyncTest('should support a `trailing` option', 4, function() {
if (!(isRhino && isModularize)) {
var withCount = 0,
withoutCount = 0;
@@ -1872,9 +2039,9 @@
}
});
- test('should work with `maxWait` option', 2, function() {
+ test('should support a `maxWait` option', 2, function() {
if (!(isRhino && isModularize)) {
- var limit = (argv || isPhantomPage) ? 1000 : 256,
+ var limit = (argv || isPhantom) ? 1000 : 256,
withCount = 0,
withoutCount = 0;
@@ -1938,7 +2105,7 @@
}
}
setTimeout(function() {
- equal(count, 2);
+ strictEqual(count, 2);
deepEqual(args, [object, 'a']);
QUnit.start();
}, 64);
@@ -1964,7 +2131,6 @@
this.a = 1;
this.c = 3;
}
-
Foo.prototype.b = 2;
deepEqual(_.defaults({ 'c': 2 }, new Foo), { 'a': 1, 'c': 2 });
});
@@ -1984,14 +2150,6 @@
var actual = _.defaults({ 'a': undefined }, { 'a': 1 });
strictEqual(actual.a, 1);
});
-
- test('should not error on `null` or `undefined` sources (test in IE < 9)', 1, function() {
- try {
- deepEqual(_.defaults({ 'a': 1 }, null, undefined, { 'a': 2, 'b': 2 }), { 'a': 1, 'b': 2 });
- } catch(e) {
- ok(false);
- }
- });
}());
/*--------------------------------------------------------------------------*/
@@ -2127,6 +2285,8 @@
QUnit.module('lodash.difference');
(function() {
+ var args = arguments;
+
test('should return the difference of the given arrays', 2, function() {
var actual = _.difference([1, 2, 3, 4, 5], [5, 2, 10]);
deepEqual(actual, [1, 3, 4]);
@@ -2136,8 +2296,8 @@
});
test('should work with large arrays', 1, function() {
- var array1 = _.range(LARGE_ARRAY_SIZE),
- array2 = array1.slice(),
+ var array1 = _.range(largeArraySize + 1),
+ array2 = _.range(largeArraySize),
a = {},
b = {},
c = {};
@@ -2145,22 +2305,73 @@
array1.push(a, b, c);
array2.push(b, c, a);
- deepEqual(_.difference(array1, array2), []);
+ deepEqual(_.difference(array1, array2), [largeArraySize]);
});
test('should work with large arrays of objects', 1, function() {
- var object = {};
+ var object1 = {},
+ object2 = {},
+ largeArray = _.times(largeArraySize, _.constant(object1));
- var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
- return object;
- });
-
- deepEqual(_.difference(largeArray, [object]), []);
+ deepEqual(_.difference([object1, object2], largeArray), [object2]);
});
- test('should ignore individual secondary values', 1, function() {
- var array = [1, null, 3];
- deepEqual(_.difference(array, null, 3), array);
+ test('should ignore values that are not arrays or `arguments` objects', 3, function() {
+ var array = [0, 1, null, 3];
+ deepEqual(_.difference(array, 3, null, { '0': 1 }), array);
+ deepEqual(_.difference(null, array, null, [2, 1]), [0, null, 3]);
+ deepEqual(_.difference(null, array, null, args), [0, null]);
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.endsWith');
+
+ (function() {
+ var string = 'abc';
+
+ test('should return `true` if a string ends with `target`', 1, function() {
+ strictEqual(_.endsWith(string, 'c'), true);
+ });
+
+ test('should return `false` if a string does not end with `target`', 1, function() {
+ strictEqual(_.endsWith(string, 'b'), false);
+ });
+
+ test('should work with a `position` argument', 1, function() {
+ strictEqual(_.endsWith(string, 'b', 2), true);
+ });
+
+ test('should work with `position` >= `string.length`', 4, function() {
+ _.each([3, 5, maxSafeInteger, Infinity], function(position) {
+ strictEqual(_.endsWith(string, 'c', position), true);
+ });
+ });
+
+ test('should treat falsey `position` values, except `undefined`, as `0`', 1, function() {
+ var expected = _.map(falsey, _.constant(true));
+
+ var actual = _.map(falsey, function(position) {
+ return _.endsWith(string, position === undefined ? 'c' : '', position);
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should treat a negative `position` as `0`', 6, function() {
+ _.each([-1, -3, -Infinity], function(position) {
+ ok(_.every(string, function(chr) {
+ return _.endsWith(string, chr, position) === false;
+ }));
+ strictEqual(_.endsWith(string, '', position), true);
+ });
+ });
+
+ test('should always return `true` when `target` is an empty string regardless of `position`', 1, function() {
+ ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, maxSafeInteger, Infinity], function(position) {
+ return _.endsWith(string, '', position, true);
+ }));
});
}());
@@ -2173,21 +2384,36 @@
unescaped = '&<>"\'\/';
test('should escape values', 1, function() {
- equal(_.escape(unescaped), escaped);
+ strictEqual(_.escape(unescaped), escaped);
});
test('should not escape the "/" character', 1, function() {
- equal(_.escape('/'), '/');
+ strictEqual(_.escape('/'), '/');
});
test('should handle strings with nothing to escape', 1, function() {
- equal(_.escape('abc'), 'abc');
+ strictEqual(_.escape('abc'), 'abc');
});
- test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() {
- strictEqual(_.escape(null), '');
- strictEqual(_.escape(undefined), '');
- strictEqual(_.escape(''), '');
+ test('should escape the same characters unescaped by `_.unescape`', 1, function() {
+ strictEqual(_.escape(_.unescape(escaped)), escaped);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.escapeRegExp');
+
+ (function() {
+ test('should escape values', 1, function() {
+ var escaped = '\\.\\*\\+\\?\\^\\$\\{\\}\\(\\)\\|\\[\\]\\/\\\\',
+ unescaped = '.*+?^${}()|[\]\/\\';
+
+ strictEqual(_.escapeRegExp(unescaped), escaped);
+ });
+
+ test('should handle strings with nothing to escape', 1, function() {
+ strictEqual(_.escapeRegExp('abc'), 'abc');
});
}());
@@ -2197,7 +2423,7 @@
(function() {
test('should return `true` for empty or falsey collections', 1, function() {
- var expected = _.map(empties, function() { return true; });
+ var expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
try {
@@ -2208,7 +2434,7 @@
deepEqual(actual, expected);
});
- test('should return `true` if the callback returns truey for all elements in the collection', 1, function() {
+ test('should return `true` if the callback returns truthy for all elements in the collection', 1, function() {
strictEqual(_.every([true, 1, 'x'], _.identity), true);
});
@@ -2234,12 +2460,13 @@
QUnit.module('source property checks');
- _.forEach(['assign', 'defaults', 'merge'], function(methodName) {
+ _.each(['assign', 'defaults', 'merge'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should not assign inherited `source` properties', 1, function() {
function Foo() {}
Foo.prototype = { 'a': 1 };
+
deepEqual(func({}, new Foo), {});
});
@@ -2261,7 +2488,7 @@
expected[1] = undefined;
- ok(1 in actual);
+ ok('1' in actual);
deepEqual(actual, expected);
});
}
@@ -2271,7 +2498,7 @@
QUnit.module('strict mode checks');
- _.forEach(['assign', 'bindAll', 'defaults'], function(methodName) {
+ _.each(['assign', 'bindAll', 'defaults'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should not throw strict mode errors', 1, function() {
@@ -2302,7 +2529,7 @@
QUnit.module('lodash.filter');
(function() {
- test('should return elements the `callback` returns truey for', 1, function() {
+ test('should return elements the `callback` returns truthy for', 1, function() {
var actual = _.filter([1, 2, 3], function(num) {
return num % 2;
});
@@ -2338,25 +2565,26 @@
/*--------------------------------------------------------------------------*/
- (function() {
- var objects = [
- { 'a': 0, 'b': 0 },
- { 'a': 1, 'b': 1 },
- { 'a': 2, 'b': 2 }
- ];
+ _.each(['find', 'findLast', 'findIndex', 'findLastIndex', 'findKey', 'findLastKey'], function(methodName) {
+ QUnit.module('lodash.' + methodName);
- _.forEach({
- 'find': [objects[1], undefined, objects[2], objects[1]],
- 'findLast': [objects[2], undefined, objects[2], objects[2]],
- 'findIndex': [1, -1, 2, 1],
- 'findLastIndex': [2, -1, 2, 2],
- 'findKey': ['1', undefined, '2', '1'],
- 'findLastKey': ['2', undefined, '2', '2']
- },
- function(expected, methodName) {
- QUnit.module('lodash.' + methodName);
+ var func = _[methodName];
- var func = _[methodName];
+ (function() {
+ var objects = [
+ { 'a': 0, 'b': 0 },
+ { 'a': 1, 'b': 1 },
+ { 'a': 2, 'b': 2 }
+ ];
+
+ var expected = ({
+ 'find': [objects[1], undefined, objects[2], objects[1]],
+ 'findLast': [objects[2], undefined, objects[2], objects[2]],
+ 'findIndex': [1, -1, 2, 1],
+ 'findLastIndex': [2, -1, 2, 2],
+ 'findKey': ['1', undefined, '2', '1'],
+ 'findLastKey': ['2', undefined, '2', '2']
+ })[methodName];
test('should return the correct value', 1, function() {
strictEqual(func(objects, function(object) { return object.a; }), expected[0]);
@@ -2367,15 +2595,7 @@
});
test('should return `' + expected[1] + '` if value is not found', 1, function() {
- strictEqual(func(objects, function(object) { return object.a == 3; }), expected[1]);
- });
-
- test('should work with an object for `collection`', 1, function() {
- var actual = _.find({ 'a': 1, 'b': 2, 'c': 3 }, function(num) {
- return num > 2;
- });
-
- equal(actual, 3);
+ strictEqual(func(objects, function(object) { return object.a === 3; }), expected[1]);
});
test('should work with an object for `callback`', 1, function() {
@@ -2391,7 +2611,7 @@
emptyValues = /Index/.test(methodName) ? _.reject(empties, _.isPlainObject) : empties,
expecting = _.map(emptyValues, function() { return expected[1]; });
- _.forEach(emptyValues, function(value) {
+ _.each(emptyValues, function(value) {
try {
actual.push(func(value, { 'a': 3 }));
} catch(e) { }
@@ -2399,13 +2619,89 @@
deepEqual(actual, expecting);
});
+ }());
- if (methodName == 'find') {
- test('should be aliased', 2, function() {
- strictEqual(_.detect, func);
- strictEqual(_.findWhere, func);
+ (function() {
+ var expected = ({
+ 'find': 1,
+ 'findLast': 2,
+ 'findKey': 'a',
+ 'findLastKey': 'b'
+ })[methodName];
+
+ if (expected != null) {
+ test('should work with an object for `collection`', 1, function() {
+ var actual = func({ 'a': 1, 'b': 2, 'c': 3 }, function(num) {
+ return num < 3;
+ });
+
+ strictEqual(actual, expected);
});
}
+ }());
+
+ (function() {
+ var expected = ({
+ 'find': 'a',
+ 'findLast': 'b',
+ 'findIndex': 0,
+ 'findLastIndex': 1
+ })[methodName];
+
+ if (expected != null) {
+ test('should work with a string for `collection`', 1, function() {
+ var actual = func('abc', function(chr, index) {
+ return index < 2;
+ });
+
+ strictEqual(actual, expected);
+ });
+ }
+ if (methodName == 'find') {
+ test('should be aliased', 1, function() {
+ strictEqual(_.detect, func);
+ });
+ }
+ }());
+ });
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.findWhere');
+
+ (function() {
+ var objects = [
+ { 'a': 1 },
+ { 'a': 1 },
+ { 'a': 1, 'b': 2 },
+ { 'a': 2, 'b': 2 },
+ { 'a': 3 }
+ ];
+
+ test('should filter by `source` properties', 6, function() {
+ strictEqual(_.findWhere(objects, { 'a': 1 }), objects[0]);
+ strictEqual(_.findWhere(objects, { 'a': 2 }), objects[3]);
+ strictEqual(_.findWhere(objects, { 'a': 3 }), objects[4]);
+ strictEqual(_.findWhere(objects, { 'b': 1 }), undefined);
+ strictEqual(_.findWhere(objects, { 'b': 2 }), objects[2]);
+ strictEqual(_.findWhere(objects, { 'a': 1, 'b': 2 }), objects[2]);
+ });
+
+ test('should work with a function for `source`', 1, function() {
+ function source() {}
+ source.a = 2;
+
+ strictEqual(_.findWhere(objects, source), objects[3]);
+ });
+
+ test('should match all elements when provided an empty `source`', 1, function() {
+ var expected = _.map(empties, _.constant(true));
+
+ var actual = _.map(empties, function(value) {
+ return _.findWhere(objects, value) === objects[0];
+ });
+
+ deepEqual(actual, expected);
});
}());
@@ -2430,14 +2726,26 @@
deepEqual(_.first(array, 2), [1, 2]);
});
+ test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value == null ? 1 : [];
+ });
+
+ var actual = _.map(falsey, function(n) {
+ return _.first(array, n);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should return an empty array when `n` < `1`', 3, function() {
- _.forEach([0, -1, -2], function(n) {
+ _.each([0, -1, -Infinity], function(n) {
deepEqual(_.first(array, n), []);
});
});
- test('should return all elements when `n` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(n) {
+ test('should return all elements when `n` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.first(array, n), array);
});
});
@@ -2593,26 +2901,22 @@
expected.push(undefined, undefined, undefined);
deepEqual(actual1, expected);
- ok(4 in actual1);
+ ok('4' in actual1);
deepEqual(actual2, expected);
- ok(4 in actual2);
+ ok('4' in actual2);
});
test('should work with extremely large arrays', 1, function() {
- var expected = Array(5e5),
- pass = true;
-
+ // test in modern browsers
if (freeze) {
try {
- var actual = _.flatten([expected]);
+ var expected = Array(5e5),
+ actual = _.flatten([expected]);
+
+ deepEqual(actual, expected)
} catch(e) {
- pass = false;
- }
- if (pass) {
- deepEqual(actual, expected);
- } else {
- ok(pass);
+ ok(false);
}
} else {
skipTest();
@@ -2655,10 +2959,11 @@
QUnit.module('forEach methods');
- _.forEach(['forEach', 'forEachRight'], function(methodName) {
- var func = _[methodName];
+ _.each(['forEach', 'forEachRight'], function(methodName) {
+ var func = _[methodName],
+ isForEach = methodName == 'forEach';
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -2672,7 +2977,7 @@
values.push(value);
});
- if (methodName == 'forEach') {
+ if (isForEach) {
deepEqual(args, ['a', 0, collection]);
deepEqual(values, ['a', 'b', 'c']);
} else {
@@ -2683,7 +2988,7 @@
});
test('`_.' + methodName + '` should be aliased', 1, function() {
- if (methodName == 'forEach') {
+ if (isForEach) {
strictEqual(_.each, _.forEach);
} else {
strictEqual(_.eachRight, _.forEachRight);
@@ -2695,7 +3000,7 @@
QUnit.module('forIn methods');
- _.forEach(['forIn', 'forInRight'], function(methodName) {
+ _.each(['forIn', 'forInRight'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` iterates over inherited properties', 1, function() {
@@ -2712,7 +3017,7 @@
QUnit.module('forOwn methods');
- _.forEach(['forOwn', 'forOwnRight'], function(methodName) {
+ _.each(['forOwn', 'forOwnRight'], function(methodName) {
var func = _[methodName];
test('iterates over the `length` property', 1, function() {
@@ -2730,15 +3035,20 @@
(function() {
var methods = [
+ 'countBy',
'every',
'filter',
- 'forEach',
'forEachRight',
'forIn',
'forInRight',
'forOwn',
'forOwnRight',
+ 'groupBy',
+ 'indexBy',
'map',
+ 'max',
+ 'min',
+ 'partition',
'reject',
'some'
];
@@ -2748,6 +3058,26 @@
'some'
];
+ var collectionMethods = [
+ 'countBy',
+ 'every',
+ 'filter',
+ 'find',
+ 'findLast',
+ 'forEach',
+ 'forEachRight',
+ 'groupBy',
+ 'indexBy',
+ 'map',
+ 'max',
+ 'min',
+ 'partition',
+ 'reduce',
+ 'reduceRight',
+ 'reject',
+ 'some'
+ ];
+
var forInMethods = [
'forIn',
'forInRight'
@@ -2775,7 +3105,7 @@
'forOwnRight'
];
- _.forEach(methods, function(methodName) {
+ _.each(methods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
@@ -2803,22 +3133,21 @@
function callback(num, index) {
actual = this[index];
}
-
func([1], callback, [2]);
- equal(actual, 2);
+ strictEqual(actual, 2);
func({ 'a': 1 }, callback, { 'a': 2 });
- equal(actual, 2);
+ strictEqual(actual, 2);
});
});
- _.forEach(_.difference(methods, boolMethods), function(methodName) {
+ _.each(_.difference(methods, boolMethods), function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
test('`_.' + methodName + '` should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
- var actual = _(array)[methodName](noop);
+ var actual = _(array)[methodName](_.noop);
ok(actual instanceof _);
}
else {
@@ -2827,7 +3156,7 @@
});
});
- _.forEach(_.difference(methods, forInMethods), function(methodName) {
+ _.each(_.difference(methods, forInMethods), function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
@@ -2841,34 +3170,56 @@
});
});
- _.forEach(iterationMethods, function(methodName) {
+ _.each(iterationMethods, function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
test('`_.' + methodName + '` should return the collection', 1, function() {
- equal(func(array, Boolean), array);
+ strictEqual(func(array, Boolean), array);
});
test('`_.' + methodName + '` should return the existing wrapper when chaining', 1, function() {
if (!isNpm) {
var wrapper = _(array);
- equal(wrapper[methodName](noop), wrapper);
+ strictEqual(wrapper[methodName](_.noop), wrapper);
}
else {
skipTest();
}
});
});
+
+ _.each(collectionMethods, function(methodName) {
+ var func = _[methodName];
+
+ test('`_.' + methodName + '` should treat objects with lengths of `0` as array-like', 1, function() {
+ var pass = true;
+ func({ 'length': 0 }, function() { pass = false; }, 0);
+ ok(pass);
+ });
+
+ test('`_.' + methodName + '` should not treat objects with negative lengths as array-like', 1, function() {
+ var pass = false;
+ func({ 'length': -1 }, function() { pass = true; }, 0);
+ ok(pass);
+ });
+
+ test('`_.' + methodName + '` should not treat objects with non-number lengths as array-like', 1, function() {
+ var pass = false;
+ func({ 'length': '0' }, function() { pass = true; }, 0);
+ ok(pass);
+ });
+ });
}());
/*--------------------------------------------------------------------------*/
QUnit.module('collection iteration bugs');
- _.forEach(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) {
+ _.each(['forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) {
var func = _[methodName];
- test('`_.' + methodName + '` fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() {
+ test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
var props = [];
func(shadowedObject, function(value, prop) { props.push(prop); });
deepEqual(props.sort(), shadowedProps);
@@ -2925,13 +3276,17 @@
QUnit.module('object assignments');
- _.forEach(['assign', 'defaults', 'merge'], function(methodName) {
+ _.each(['assign', 'defaults', 'merge'], function(methodName) {
var func = _[methodName];
+ test('`_.' + methodName + '` should return `undefined` when no destination object is provided', 1, function() {
+ strictEqual(func(), undefined);
+ });
+
test('`_.' + methodName + '` should return the existing wrapper when chaining', 1, function() {
if (!isNpm) {
var wrapper = _({ 'a': 1 });
- equal(wrapper[methodName]({ 'b': 2 }), wrapper);
+ strictEqual(wrapper[methodName]({ 'b': 2 }), wrapper);
}
else {
skipTest();
@@ -2961,10 +3316,9 @@
test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
function Foo() {}
- Foo.prototype.c = 3;
-
Foo.a = 1;
Foo.b = 2;
+ Foo.prototype.c = 3;
var expected = { 'a': 1, 'b': 2 };
deepEqual(func({}, Foo), expected);
@@ -2977,9 +3331,31 @@
var array = [{ 'b': 2 }, { 'c': 3 }];
deepEqual(_.reduce(array, func, { 'a': 1}), { 'a': 1, 'b': 2, 'c': 3 });
});
+
+ test('`_.' + methodName + '` should not error on nullish sources (test in IE < 9)', 1, function() {
+ try {
+ deepEqual(func({ 'a': 1 }, undefined, { 'b': 2 }, null), { 'a': 1, 'b': 2 });
+ } catch(e) {
+ ok(false);
+ }
+ });
+
+ test('`_.' + methodName + '` should not error when `object` is nullish and source objects are provided', 1, function() {
+ var expected = _.times(2, _.constant(true));
+
+ var actual = _.map([null, undefined], function(value) {
+ try {
+ return _.isEqual(func(value, { 'a': 1 }), value);
+ } catch(e) {
+ return false;
+ }
+ });
+
+ deepEqual(actual, expected);
+ });
});
- _.forEach(['assign', 'merge'], function(methodName) {
+ _.each(['assign', 'merge'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should pass the correct `callback` arguments', 2, function() {
@@ -3026,7 +3402,7 @@
QUnit.module('exit early');
- _.forEach(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) {
+ _.each(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight'], function(methodName) {
var func = _[methodName];
if (!func) {
return;
@@ -3044,7 +3420,7 @@
values = [];
func(object, function(value) { values.push(value); return false; });
- equal(values.length, 1);
+ strictEqual(values.length, 1);
});
});
@@ -3058,7 +3434,7 @@
stringObject = Object(stringLiteral),
expected = [stringLiteral, stringObject];
- var largeArray = _.times(LARGE_ARRAY_SIZE, function(count) {
+ var largeArray = _.times(largeArraySize, function(count) {
return count % 2 ? stringObject : stringLiteral;
});
@@ -3068,7 +3444,7 @@
deepEqual(_.without.apply(_, [largeArray].concat(largeArray)), []);
});
- test('lodash.memoize should memoize values resolved to the `__proto__` key', 1, function() {
+ test('lodash.memoize should support values that resolve to the `__proto__` key', 1, function() {
var count = 0,
memoized = _.memoize(function() { return ++count; });
@@ -3084,7 +3460,7 @@
(function() {
test('should return the function names of an object', 1, function() {
- var object = { 'a': 'a', 'b': _.identity, 'c': /x/, 'd': _.forEach };
+ var object = { 'a': 'a', 'b': _.identity, 'c': /x/, 'd': _.each };
deepEqual(_.functions(object), ['b', 'd']);
});
@@ -3093,8 +3469,7 @@
this.a = _.identity;
this.b = 'b'
}
-
- Foo.prototype.c = noop;
+ Foo.prototype.c = _.noop;
deepEqual(_.functions(new Foo), ['a', 'c']);
});
@@ -3196,7 +3571,7 @@
test('should return `false` for primitives', 1, function() {
var values = falsey.concat(1, 'a'),
- expected = _.map(values, function() { return false; });
+ expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
return _.has(value, 'valueOf');
@@ -3272,43 +3647,54 @@
var array = [1, 2, 3, 1, 2, 3];
test('should return the index of the first matched value', 1, function() {
- equal(_.indexOf(array, 3), 2);
+ strictEqual(_.indexOf(array, 3), 2);
});
test('should return `-1` for an unmatched value', 4, function() {
- equal(_.indexOf(array, 4), -1);
- equal(_.indexOf(array, 4, true), -1);
+ strictEqual(_.indexOf(array, 4), -1);
+ strictEqual(_.indexOf(array, 4, true), -1);
var empty = [];
- equal(_.indexOf(empty, undefined), -1);
- equal(_.indexOf(empty, undefined, true), -1);
+ strictEqual(_.indexOf(empty, undefined), -1);
+ strictEqual(_.indexOf(empty, undefined, true), -1);
});
test('should work with a positive `fromIndex`', 1, function() {
- equal(_.indexOf(array, 1, 2), 3);
+ strictEqual(_.indexOf(array, 1, 2), 3);
});
- test('should work with `fromIndex` >= `array.length`', 6, function() {
- _.forEach([6, 8], function(fromIndex) {
- equal(_.indexOf(array, 1, fromIndex), -1);
- equal(_.indexOf(array, undefined, fromIndex), -1);
- equal(_.indexOf(array, '', fromIndex), -1);
+ test('should work with `fromIndex` >= `array.length`', 12, function() {
+ _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
+ strictEqual(_.indexOf(array, 1, fromIndex), -1);
+ strictEqual(_.indexOf(array, undefined, fromIndex), -1);
+ strictEqual(_.indexOf(array, '', fromIndex), -1);
});
});
- test('should work with a negative `fromIndex`', 1, function() {
- equal(_.indexOf(array, 2, -3), 4);
+ 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 work with a negative `fromIndex` <= `-array.length`', 2, function() {
- strictEqual(_.indexOf(array, 1, -6), 0);
- strictEqual(_.indexOf(array, 2, -8), 1);
- });
-
- test('should ignore non-number `fromIndex` values', 1, function() {
+ test('should treat non-number `fromIndex` values as `0`', 1, function() {
strictEqual(_.indexOf([1, 2, 3], 1, '1'), 0);
});
+ test('should work with a negative `fromIndex`', 1, function() {
+ strictEqual(_.indexOf(array, 2, -3), 4);
+ });
+
+ test('should work with a negative `fromIndex` <= `-array.length`', 3, function() {
+ _.each([-6, -8, -Infinity], function(fromIndex) {
+ strictEqual(_.indexOf(array, 1, fromIndex), 0);
+ });
+ });
+
test('should work with `isSorted`', 1, function() {
strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
});
@@ -3319,6 +3705,8 @@
QUnit.module('custom `_.indexOf` methods');
(function() {
+ function Foo() {}
+
function custom(array, value, fromIndex) {
var index = (fromIndex || 0) - 1,
length = array.length;
@@ -3332,23 +3720,22 @@
return -1;
}
- function Foo() {}
-
var array = [1, new Foo, 3, new Foo],
indexOf = _.indexOf;
- var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
+ var largeArray = _.times(largeArraySize, function() {
return new Foo;
});
- test('`_.contains` should work with a custom `_.indexOf` method', 1, function() {
+ test('`_.contains` should work with a custom `_.indexOf` method', 2, function() {
if (!isModularize) {
_.indexOf = custom;
ok(_.contains(array, new Foo));
+ ok(_.contains({ 'a': 1, 'b': new Foo, 'c': 3 }, new Foo));
_.indexOf = indexOf;
}
else {
- skipTest();
+ skipTest(2);
}
});
@@ -3403,7 +3790,7 @@
];
test('should accept a falsey `array` argument', 1, function() {
- var expected = _.map(falsey, function() { return []; });
+ var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
@@ -3426,14 +3813,26 @@
deepEqual(_.initial([]), []);
});
+ test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value == null ? [1, 2] : array;
+ });
+
+ var actual = _.map(falsey, function(n) {
+ return _.initial(array, n);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should return all elements when `n` < `1`', 3, function() {
- _.forEach([0, -1, -2], function(n) {
+ _.each([0, -1, -Infinity], function(n) {
deepEqual(_.initial(array, n), array);
});
});
- test('should return an empty array when `n` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(n) {
+ test('should return an empty array when `n` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.initial(array, n), []);
});
});
@@ -3485,25 +3884,32 @@
QUnit.module('lodash.intersection');
(function() {
+ var args = arguments;
+
test('should return the intersection of the given arrays', 1, function() {
var actual = _.intersection([1, 3, 2], [5, 2, 1, 4], [2, 1]);
deepEqual(actual, [1, 2]);
});
- test('should return an array of unique values', 1, function() {
- var actual = _.intersection([1, 1, 3, 2, 2], [5, 2, 2, 1, 4], [2, 1, 1]);
- deepEqual(actual, [1, 2]);
+ test('should return an array of unique values', 2, function() {
+ var array = [1, 1, 3, 2, 2];
+ deepEqual(_.intersection(array, [5, 2, 2, 1, 4], [2, 1, 1]), [1, 2]);
+ deepEqual(_.intersection(array), [1, 3, 2]);
});
test('should work with large arrays of objects', 1, function() {
var object = {},
- expected = [object];
+ largeArray = _.times(largeArraySize, _.constant(object));
- var largeArray = _.times(LARGE_ARRAY_SIZE, function() {
- return object;
- });
+ deepEqual(_.intersection([object], largeArray), [object]);
+ });
- deepEqual(_.intersection(expected, largeArray), expected);
+ test('should work with large arrays of objects', 2, function() {
+ var object = {},
+ largeArray = _.times(largeArraySize, _.constant(object));
+
+ deepEqual(_.intersection([object], largeArray), [object]);
+ deepEqual(_.intersection(_.range(largeArraySize), null, [1]), [1]);
});
test('should return a wrapped value when chaining', 2, function() {
@@ -3517,10 +3923,13 @@
}
});
- test('should ignore individual secondary values', 1, function() {
- deepEqual(_.intersection([1, null, 3], 3, null), []);
+ test('should ignore values that are not arrays or `arguments` objects', 3, function() {
+ var array = [0, 1, null, 3];
+ deepEqual(_.intersection(array, 3, null, { '0': 1 }), array);
+ deepEqual(_.intersection(null, array, null, [2, 1]), [1]);
+ deepEqual(_.intersection(null, array, null, args), [1, 3]);
});
- }());
+ }(1, 2, 3));
/*--------------------------------------------------------------------------*/
@@ -3558,8 +3967,8 @@
(function() {
test('should invoke a methods on each element of a collection', 1, function() {
- var actual = _.invoke(['a', 'b', 'c'], 'toUpperCase');
- deepEqual(actual, ['A', 'B', 'C']);
+ var array = ['a', 'b', 'c'];
+ deepEqual( _.invoke(array, 'toUpperCase'), ['A', 'B', 'C']);
});
test('should work with a function `methodName` argument', 1, function() {
@@ -3574,6 +3983,15 @@
var object = { 'a': 1, 'b': 2, 'c': 3 };
deepEqual(_.invoke(object, 'toFixed', 1), ['1.0', '2.0', '3.0']);
});
+
+ test('should treat number values for `collection` as empty', 1, function() {
+ deepEqual(_.invoke(1), []);
+ });
+
+ test('should work with nullish elements', 1, function() {
+ var array = ['a', null, undefined, 'd'];
+ deepEqual(_.invoke(array, 'toUpperCase'), ['A', undefined, undefined, 'D']);
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -3587,8 +4005,8 @@
strictEqual(_.isArguments(args), true);
});
- test('should return `false` for non `arguments` objects', 9, function() {
- var expected = _.map(falsey, function() { return false; });
+ test('should return `false` for non `arguments` objects', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isArguments(value) : _.isArguments();
@@ -3597,9 +4015,10 @@
strictEqual(_.isArguments([1, 2, 3]), false);
strictEqual(_.isArguments(true), false);
strictEqual(_.isArguments(new Date), false);
+ strictEqual(_.isArguments(new Error), false);
strictEqual(_.isArguments(_), false);
- strictEqual(_.isArguments({ '0': 1, 'callee': noop, 'length': 1 }), false);
- strictEqual(_.isArguments(0), false);
+ strictEqual(_.isArguments({ '0': 1, 'callee': _.noop, 'length': 1 }), false);
+ strictEqual(_.isArguments(1), false);
strictEqual(_.isArguments(/x/), false);
strictEqual(_.isArguments('a'), false);
@@ -3627,8 +4046,8 @@
strictEqual(_.isArray([1, 2, 3]), true);
});
- test('should return `false` for non arrays', 9, function() {
- var expected = _.map(falsey, function() { return false; });
+ test('should return `false` for non arrays', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isArray(value) : _.isArray();
@@ -3637,9 +4056,10 @@
strictEqual(_.isArray(args), false);
strictEqual(_.isArray(true), false);
strictEqual(_.isArray(new Date), false);
+ strictEqual(_.isArray(new Error), false);
strictEqual(_.isArray(_), false);
strictEqual(_.isArray({ '0': 1, 'length': 1 }), false);
- strictEqual(_.isArray(0), false);
+ strictEqual(_.isArray(1), false);
strictEqual(_.isArray(/x/), false);
strictEqual(_.isArray('a'), false);
@@ -3670,7 +4090,7 @@
strictEqual(_.isBoolean(new Boolean(false)), true);
});
- test('should return `false` for non booleans', 9, function() {
+ test('should return `false` for non booleans', 10, function() {
var expected = _.map(falsey, function(value) { return value === false; });
var actual = _.map(falsey, function(value, index) {
@@ -3680,9 +4100,10 @@
strictEqual(_.isBoolean(args), false);
strictEqual(_.isBoolean([1, 2, 3]), false);
strictEqual(_.isBoolean(new Date), false);
+ strictEqual(_.isBoolean(new Error), false);
strictEqual(_.isBoolean(_), false);
strictEqual(_.isBoolean({ 'a': 1 }), false);
- strictEqual(_.isBoolean(0), false);
+ strictEqual(_.isBoolean(1), false);
strictEqual(_.isBoolean(/x/), false);
strictEqual(_.isBoolean('a'), false);
@@ -3710,8 +4131,8 @@
strictEqual(_.isDate(new Date), true);
});
- test('should return `false` for non dates', 9, function() {
- var expected = _.map(falsey, function() { return false; });
+ test('should return `false` for non dates', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isDate(value) : _.isDate();
@@ -3720,9 +4141,10 @@
strictEqual(_.isDate(args), false);
strictEqual(_.isDate([1, 2, 3]), false);
strictEqual(_.isDate(true), false);
+ strictEqual(_.isDate(new Error), false);
strictEqual(_.isDate(_), false);
strictEqual(_.isDate({ 'a': 1 }), false);
- strictEqual(_.isDate(0), false);
+ strictEqual(_.isDate(1), false);
strictEqual(_.isDate(/x/), false);
strictEqual(_.isDate('a'), false);
@@ -3744,6 +4166,8 @@
QUnit.module('lodash.isElement');
(function() {
+ var args = arguments;
+
function Element() {
this.nodeType = 1;
}
@@ -3772,7 +4196,28 @@
}
});
- test('should work with elements from another realm', 1, function() {
+ test('should return `false` for non DOM elements', 11, function() {
+ var expected = _.map(falsey, _.constant(false));
+
+ var actual = _.map(falsey, function(value, index) {
+ return index ? _.isElement(value) : _.isElement();
+ });
+
+ strictEqual(_.isElement(args), false);
+ strictEqual(_.isElement([1, 2, 3]), false);
+ strictEqual(_.isElement(true), false);
+ strictEqual(_.isElement(new Date), false);
+ strictEqual(_.isElement(new Error), false);
+ strictEqual(_.isElement(_), false);
+ strictEqual(_.isElement({ 'a': 1 }), false);
+ strictEqual(_.isElement(1), false);
+ strictEqual(_.isElement(/x/), false);
+ strictEqual(_.isElement('a'), false);
+
+ deepEqual(actual, expected);
+ });
+
+ test('should work with DOM elements from another realm', 1, function() {
if (_._element) {
strictEqual(_.isElement(_._element), true);
}
@@ -3780,7 +4225,7 @@
skipTest();
}
});
- }());
+ }(1, 2, 3));
/*--------------------------------------------------------------------------*/
@@ -3790,7 +4235,7 @@
var args = arguments;
test('should return `true` for empty or falsey values', 3, function() {
- var expected = _.map(empties, function() { return true; });
+ var expected = _.map(empties, _.constant(true));
var actual = _.map(empties, function(value) {
return _.isEmpty(value);
@@ -3807,23 +4252,14 @@
strictEqual(_.isEmpty('a'), false);
});
- test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() {
- equal(_.isEmpty(shadowedObject), false);
- });
-
- test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
- function Foo() {}
- Foo.prototype.a = 1;
- strictEqual(_.isEmpty(Foo), true);
-
- Foo.prototype = { 'a': 1 };
- strictEqual(_.isEmpty(Foo), true);
- });
-
test('should work with an object that has a `length` property', 1, function() {
strictEqual(_.isEmpty({ 'length': 0 }), false);
});
+ test('should work with `arguments` objects (test in IE < 9)', 1, function() {
+ strictEqual(_.isEmpty(args), false);
+ });
+
test('should work with jQuery/MooTools DOM query collections', 1, function() {
function Foo(elements) { push.apply(this, elements); }
Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice };
@@ -3831,12 +4267,36 @@
strictEqual(_.isEmpty(new Foo([])), true);
});
- test('should work with `arguments` objects (test in IE < 9)', 1, function() {
- if (!isPhantomPage) {
- strictEqual(_.isEmpty(args), false);
- } else {
- skipTest();
- }
+ test('should not treat objects with negative lengths as array-like', 1, function() {
+ function Foo() {}
+ Foo.prototype.length = -1;
+
+ strictEqual(_.isEmpty(new Foo), true);
+ });
+
+ test('should not treat objects with lengths larger than `maxSafeInteger` as array-like', 1, function() {
+ function Foo() {}
+ Foo.prototype.length = maxSafeInteger + 1;
+
+ strictEqual(_.isEmpty(new Foo), true);
+ });
+
+ test('should not treat objects with non-number lengths as array-like', 1, function() {
+ strictEqual(_.isEmpty({ 'length': '0' }), false);
+ });
+
+ test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
+ strictEqual(_.isEmpty(shadowedObject), false);
+ });
+
+ test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
+ function Foo() {}
+ Foo.prototype.a = 1;
+
+ strictEqual(_.isEmpty(Foo), true);
+
+ Foo.prototype = { 'a': 1 };
+ strictEqual(_.isEmpty(Foo), true);
});
test('should return an unwrapped value when intuitively chaining', 1, function() {
@@ -3890,14 +4350,14 @@
var primitive,
object = { 'toString': function() { return primitive; } },
values = [true, null, 1, 'a', undefined],
- expected = _.map(values, function() { return false; });
+ expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value) {
primitive = value;
return _.isEqual(object, value);
});
- ok(actual, expected);
+ deepEqual(actual, expected);
});
test('should perform comparisons between arrays', 6, function() {
@@ -4011,7 +4471,7 @@
'f': ['a', new String('b'), 'c'],
'g': new Boolean(false),
'h': new Date(2012, 4, 23),
- 'i': noop,
+ 'i': _.noop,
'j': 'a'
}
};
@@ -4025,7 +4485,7 @@
'f': ['a', 'b', 'c'],
'g': false,
'h': new Date(2012, 4, 23),
- 'i': noop,
+ 'i': _.noop,
'j': 'a'
}
};
@@ -4034,9 +4494,7 @@
});
test('should perform comparisons between object instances', 4, function() {
- function Foo() {
- this.value = 1;
- }
+ function Foo() { this.value = 1; }
Foo.prototype.value = 1;
function Bar() {
@@ -4082,7 +4540,7 @@
strictEqual(_.isEqual(args1, args2), true);
- if (!isPhantomPage) {
+ if (!isPhantom) {
strictEqual(_.isEqual(args1, args3), false);
}
else {
@@ -4090,7 +4548,24 @@
}
});
- test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() {
+ test('should treat `arguments` objects like `Object` objects', 2, function() {
+ var args = (function() { return arguments; }(1, 2, 3)),
+ object = { '0': 1, '1': 2, '2': 3, 'length': 3 };
+
+ function Foo() {}
+ Foo.prototype = object;
+
+ strictEqual(_.isEqual(args, object), true);
+
+ if (!isPhantom) {
+ strictEqual(_.isEqual(args, new Foo), false);
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
strictEqual(_.isEqual(shadowedObject, {}), false);
});
@@ -4236,11 +4711,11 @@
var actual = _.isEqual('a', 'a', function() { return 'a'; });
strictEqual(actual, true);
- var expected = _.map(falsey, function() { return false; });
+ var expected = _.map(falsey, _.constant(false));
actual = [];
- _.forEach(falsey, function(value) {
- actual.push(_.isEqual('a', 'b', function() { return value; }));
+ _.each(falsey, function(value) {
+ actual.push(_.isEqual('a', 'b', _.constant(value)));
});
deepEqual(actual, expected);
@@ -4263,13 +4738,13 @@
function Foo() { this.a = 1; }
Foo.prototype.constructor = null;
- var other = { 'a': 1 };
- strictEqual(_.isEqual(new Foo, other), false);
+ var otherObject = { 'a': 1 };
+ strictEqual(_.isEqual(new Foo, otherObject), false);
if (create) {
- var object = Object.create(null);
+ var object = create(null);
object.a = 1;
- strictEqual(_.isEqual(object, other), true);
+ strictEqual(_.isEqual(object, otherObject), true);
}
else {
skipTest();
@@ -4344,10 +4819,80 @@
skipTest();
}
});
+
+ test('should not error on DOM elements', 1, function() {
+ if (document) {
+ var element1 = document.createElement('div'),
+ element2 = element1.cloneNode(true);
+
+ try {
+ strictEqual(_.isEqual(element1, element2), false);
+ } catch(e) {
+ ok(false);
+ }
+ }
+ else {
+ skipTest();
+ }
+ });
}());
/*--------------------------------------------------------------------------*/
+ QUnit.module('lodash.isError');
+
+ (function() {
+ var args = arguments;
+
+ test('should return `true` for error objects', 1, function() {
+ var errors = [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],
+ expected = _.map(errors, _.constant(true));
+
+ var actual = _.map(errors, function(error) {
+ return _.isError(error) === true;
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should return `false` for non-error objects', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
+
+ var actual = _.map(falsey, function(value, index) {
+ return index ? _.isError(value) : _.isError();
+ });
+
+ strictEqual(_.isError(args), false);
+ strictEqual(_.isError([1, 2, 3]), false);
+ strictEqual(_.isError(true), false);
+ strictEqual(_.isError(new Date), false);
+ strictEqual(_.isError(_), false);
+ strictEqual(_.isError({ 'a': 1 }), false);
+ strictEqual(_.isError(1), false);
+ strictEqual(_.isError(/x/), false);
+ strictEqual(_.isError('a'), false);
+
+ deepEqual(actual, expected);
+ });
+
+ test('should work with an error object from another realm', 1, function() {
+ if (_._object) {
+ var expected = _.map(_._errors, _.constant(true));
+
+ var actual = _.map(_._errors, function(error) {
+ return _.isError(error) === true;
+ });
+
+ deepEqual(actual, expected);
+ }
+ else {
+ skipTest();
+ }
+ });
+ }(1, 2, 3));
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash.isFinite');
(function() {
@@ -4365,12 +4910,13 @@
strictEqual(_.isFinite(-Infinity), false);
});
- test('should return `false` for non-numeric values', 8, function() {
+ test('should return `false` for non-numeric values', 9, function() {
strictEqual(_.isFinite(null), false);
strictEqual(_.isFinite(undefined), false);
strictEqual(_.isFinite([]), false);
strictEqual(_.isFinite(true), false);
strictEqual(_.isFinite(new Date), false);
+ strictEqual(_.isFinite(new Error), false);
strictEqual(_.isFinite(''), false);
strictEqual(_.isFinite(' '), false);
strictEqual(_.isFinite('2px'), false);
@@ -4403,8 +4949,8 @@
strictEqual(_.isFunction(_), true);
});
- test('should return `false` for non functions', 9, function() {
- var expected = _.map(falsey, function() { return false; });
+ test('should return `false` for non functions', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isFunction(value) : _.isFunction();
@@ -4414,8 +4960,9 @@
strictEqual(_.isFunction([1, 2, 3]), false);
strictEqual(_.isFunction(true), false);
strictEqual(_.isFunction(new Date), false);
+ strictEqual(_.isFunction(new Error), false);
strictEqual(_.isFunction({ 'a': 1 }), false);
- strictEqual(_.isFunction(0), false);
+ strictEqual(_.isFunction(1), false);
strictEqual(_.isFunction(/x/), false);
strictEqual(_.isFunction('a'), false);
@@ -4444,7 +4991,7 @@
strictEqual(_.isNaN(new Number(NaN)), true);
});
- test('should return `false` for non NaNs', 10, function() {
+ test('should return `false` for non NaNs', 11, function() {
var expected = _.map(falsey, function(value) { return value !== value; });
var actual = _.map(falsey, function(value, index) {
@@ -4455,9 +5002,10 @@
strictEqual(_.isNaN([1, 2, 3]), false);
strictEqual(_.isNaN(true), false);
strictEqual(_.isNaN(new Date), false);
+ strictEqual(_.isNaN(new Error), false);
strictEqual(_.isNaN(_), false);
strictEqual(_.isNaN({ 'a': 1 }), false);
- strictEqual(_.isNaN(0), false);
+ strictEqual(_.isNaN(1), false);
strictEqual(_.isNaN(/x/), false);
strictEqual(_.isNaN('a'), false);
@@ -4485,7 +5033,7 @@
strictEqual(_.isNull(null), true);
});
- test('should return `false` for non nulls', 10, function() {
+ test('should return `false` for non nulls', 11, function() {
var expected = _.map(falsey, function(value) { return value === null; });
var actual = _.map(falsey, function(value, index) {
@@ -4496,9 +5044,10 @@
strictEqual(_.isNull([1, 2, 3]), false);
strictEqual(_.isNull(true), false);
strictEqual(_.isNull(new Date), false);
+ strictEqual(_.isNull(new Error), false);
strictEqual(_.isNull(_), false);
strictEqual(_.isNull({ 'a': 1 }), false);
- strictEqual(_.isNull(0), false);
+ strictEqual(_.isNull(1), false);
strictEqual(_.isNull(/x/), false);
strictEqual(_.isNull('a'), false);
@@ -4527,7 +5076,7 @@
strictEqual(_.isNumber(new Number(0)), true);
});
- test('should return `false` for non numbers', 9, function() {
+ test('should return `false` for non numbers', 10, function() {
var expected = _.map(falsey, function(value) { return typeof value == 'number'; });
var actual = _.map(falsey, function(value, index) {
@@ -4538,6 +5087,7 @@
strictEqual(_.isNumber([1, 2, 3]), false);
strictEqual(_.isNumber(true), false);
strictEqual(_.isNumber(new Date), false);
+ strictEqual(_.isNumber(new Error), false);
strictEqual(_.isNumber(_), false);
strictEqual(_.isNumber({ 'a': 1 }), false);
strictEqual(_.isNumber(/x/), false);
@@ -4567,11 +5117,12 @@
(function() {
var args = arguments;
- test('should return `true` for objects', 10, function() {
+ test('should return `true` for objects', 11, function() {
strictEqual(_.isObject(args), true);
strictEqual(_.isObject([1, 2, 3]), true);
strictEqual(_.isObject(new Boolean(false)), true);
strictEqual(_.isObject(new Date), true);
+ strictEqual(_.isObject(new Error), true);
strictEqual(_.isObject(_), true);
strictEqual(_.isObject({ 'a': 1 }), true);
strictEqual(_.isObject(new Number(0)), true);
@@ -4587,7 +5138,7 @@
test('should return `false` for non objects', 1, function() {
var values = falsey.concat('a', true),
- expected = _.map(values, function() { return false; });
+ expected = _.map(values, _.constant(false));
var actual = _.map(values, function(value, index) {
return index ? _.isObject(value) : _.isObject();
@@ -4628,7 +5179,7 @@
// 2: Initial check with object, this is the other half of the trigger
_.isObject(obj);
- equal(_.isObject(str), false);
+ strictEqual(_.isObject(str), false);
});
}(1, 2, 3));
@@ -4651,7 +5202,7 @@
strictEqual(_.isPlainObject(new Foo(1)), false);
});
- test('should return `true` for objects a [[Prototype]] of `null`', 1, function() {
+ test('should return `true` for objects with a `[[Prototype]]` of `null`', 1, function() {
if (create) {
strictEqual(_.isPlainObject(create(null)), true);
} else {
@@ -4681,14 +5232,14 @@
}
});
- test('should return `false` for Object objects without a [[Class]] of "Object"', 3, function() {
+ test('should return `false` for Object objects without a `[[Class]]` of "Object"', 3, function() {
strictEqual(_.isPlainObject(arguments), false);
strictEqual(_.isPlainObject(Error), false);
strictEqual(_.isPlainObject(Math), false);
});
test('should return `false` for non objects', 3, function() {
- var expected = _.map(falsey, function() { return false; });
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isPlainObject(value) : _.isPlainObject();
@@ -4721,8 +5272,8 @@
strictEqual(_.isRegExp(RegExp('x')), true);
});
- test('should return `false` for non regexes', 9, function() {
- var expected = _.map(falsey, function(value) { return false; });
+ test('should return `false` for non regexes', 10, function() {
+ var expected = _.map(falsey, _.constant(false));
var actual = _.map(falsey, function(value, index) {
return index ? _.isRegExp(value) : _.isRegExp();
@@ -4732,9 +5283,10 @@
strictEqual(_.isRegExp([1, 2, 3]), false);
strictEqual(_.isRegExp(true), false);
strictEqual(_.isRegExp(new Date), false);
+ strictEqual(_.isRegExp(new Error), false);
strictEqual(_.isRegExp(_), false);
strictEqual(_.isRegExp({ 'a': 1 }), false);
- strictEqual(_.isRegExp(0), false);
+ strictEqual(_.isRegExp(1), false);
strictEqual(_.isRegExp('a'), false);
deepEqual(actual, expected);
@@ -4762,7 +5314,7 @@
strictEqual(_.isString(new String('a')), true);
});
- test('should return `false` for non strings', 9, function() {
+ test('should return `false` for non strings', 10, function() {
var expected = _.map(falsey, function(value) { return value === ''; });
var actual = _.map(falsey, function(value, index) {
@@ -4773,9 +5325,10 @@
strictEqual(_.isString([1, 2, 3]), false);
strictEqual(_.isString(true), false);
strictEqual(_.isString(new Date), false);
+ strictEqual(_.isString(new Error), false);
strictEqual(_.isString(_), false);
strictEqual(_.isString({ '0': 1, 'length': 1 }), false);
- strictEqual(_.isString(0), false);
+ strictEqual(_.isString(1), false);
strictEqual(_.isString(/x/), false);
deepEqual(actual, expected);
@@ -4803,20 +5356,21 @@
strictEqual(_.isUndefined(undefined), true);
});
- test('should return `false` for non `undefined` values', 10, function() {
+ test('should return `false` for non `undefined` values', 11, function() {
var expected = _.map(falsey, function(value) { return value === undefined; });
var actual = _.map(falsey, function(value, index) {
- return _.isUndefined(value);
+ return index ? _.isUndefined(value) : _.isUndefined();
});
strictEqual(_.isUndefined(args), false);
strictEqual(_.isUndefined([1, 2, 3]), false);
strictEqual(_.isUndefined(true), false);
strictEqual(_.isUndefined(new Date), false);
+ strictEqual(_.isUndefined(new Error), false);
strictEqual(_.isUndefined(_), false);
strictEqual(_.isUndefined({ 'a': 1 }), false);
- strictEqual(_.isUndefined(0), false);
+ strictEqual(_.isUndefined(1), false);
strictEqual(_.isUndefined(/x/), false);
strictEqual(_.isUndefined('a'), false);
@@ -4838,13 +5392,13 @@
QUnit.module('isType checks');
(function() {
- test('should return `false` for subclassed values', 7, function() {
+ test('should return `false` for subclassed values', 8, function() {
var funcs = [
- 'isArray', 'isBoolean', 'isDate', 'isFunction',
- 'isNumber', 'isRegExp', 'isString'
+ 'isArray', 'isBoolean', 'isDate', 'isError',
+ 'isFunction', 'isNumber', 'isRegExp', 'isString'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
function Foo() {}
Foo.prototype = root[methodName.slice(2)].prototype;
@@ -4868,7 +5422,7 @@
'isObject', 'isNull', 'isNumber', 'isRegExp', 'isString', 'isUndefined'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
var pass = true;
try {
_[methodName](xml);
@@ -4886,52 +5440,146 @@
/*--------------------------------------------------------------------------*/
- QUnit.module('lodash.keys');
+ QUnit.module('keys methods');
- (function() {
- var args = arguments;
+ _.each(['keys', 'keysIn'], function(methodName) {
+ var args = arguments,
+ func = _[methodName],
+ isKeys = methodName == 'keys';
- test('should return the keys of an object', 1, function() {
- var object = { 'a': 1, 'b': 1 };
- deepEqual(_.keys(object), ['a', 'b']);
+ test('`_.' + methodName + '` should return the keys of an object', 1, function() {
+ var object = { 'a': 1, 'b': 1 },
+ actual = func(object);
+
+ deepEqual(actual.sort(), ['a', 'b']);
});
- test('should work with sparse arrays', 1, function() {
+ test('`_.' + methodName + '` should treat sparse arrays as dense', 1, function() {
var array = [1];
array[2] = 3;
- deepEqual(_.keys(array), ['0', '2']);
+
+ var actual = func(array);
+ deepEqual(actual.sort(), ['0', '1', '2']);
});
- test('should work with `arguments` objects (test in IE < 9)', 1, function() {
- if (!isPhantomPage) {
- deepEqual(_.keys(args), ['0', '1', '2']);
+ test('`_.' + methodName + '` should custom properties on arrays', 1, function() {
+ var array = [1];
+ array.a = 1;
+
+ var actual = func(array);
+ deepEqual(actual.sort(), ['0', 'a']);
+ });
+
+ test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of arrays', 1, function() {
+ Array.prototype.a = 1;
+ var expected = isKeys ? ['0'] : ['0', 'a'],
+ actual = func([1]);
+
+ deepEqual(actual.sort(), expected);
+ delete Array.prototype.a;
+ });
+
+ test('`_.' + methodName + '` should work with `arguments` objects (test in IE < 9)', 1, function() {
+ if (!isPhantom) {
+ var actual = func(args);
+ deepEqual(actual.sort(), ['0', '1', '2']);
} else {
skipTest();
}
});
- test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 2, function() {
+ test('`_.' + methodName + '` should custom properties on `arguments` objects', 1, function() {
+ if (!isPhantom) {
+ args.a = 1;
+ var actual = func(args);
+
+ deepEqual(actual.sort(), ['0', '1', '2', 'a']);
+ delete args.a;
+ } else {
+ skipTest();
+ }
+ });
+
+ test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of `arguments` objects', 1, function() {
+ if (!isPhantom) {
+ Object.prototype.a = 1;
+ var expected = isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a'],
+ actual = func(args);
+
+ deepEqual(actual.sort(), expected);
+ delete Object.prototype.a;
+ } else {
+ skipTest();
+ }
+ });
+
+ test('`_.' + methodName + '` should work with string objects (test in IE < 9)', 1, function() {
+ var actual = func(Object('abc'));
+ deepEqual(actual.sort(), ['0', '1', '2']);
+ });
+
+ test('`_.' + methodName + '` should custom properties on string objects', 1, function() {
+ var object = Object('a');
+ object.a = 1;
+
+ var actual = func(object);
+ deepEqual(actual.sort(), ['0', 'a']);
+ });
+
+ test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties of string objects', 1, function() {
+ String.prototype.a = 1;
+ var expected = isKeys ? ['0'] : ['0', 'a'],
+ actual = func(Object('a'));
+
+ deepEqual(actual.sort(), expected);
+ delete String.prototype.a;
+ });
+
+ test('`_.' + methodName + '` fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
+ var actual = func(shadowedObject);
+ deepEqual(actual.sort(), shadowedProps);
+ });
+
+ test('`_.' + methodName + '` skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
+ function Foo() {}
+ Foo.a = 1;
+ Foo.b = 2;
+ Foo.prototype.c = 3;
+
+ var expected = ['a', 'b'],
+ actual = func(Foo);
+
+ deepEqual(actual.sort(), expected);
+
+ Foo.prototype = { 'c': 3 };
+ actual = func(Foo);
+ deepEqual(actual.sort(), expected);
+ });
+
+ test('`_.' + methodName + '` skips the `constructor` property on prototype objects', 2, function() {
function Foo() {}
Foo.prototype.a = 1;
- deepEqual(_.keys(Foo.prototype), ['a']);
- deepEqual(_.keys(shadowedObject).sort(), shadowedProps);
+ var expected = ['a'];
+ deepEqual(func(Foo.prototype), ['a']);
+
+ Foo.prototype = { 'constructor': Foo, 'a': 1 };
+ deepEqual(func(Foo.prototype), ['a']);
});
- test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', 2, function() {
- function Foo() {}
+ test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() {
+ function Foo() {
+ this.a = 1;
+ this.b = 2;
+ }
Foo.prototype.c = 3;
- Foo.a = 1;
- Foo.b = 2;
+ var expected = isKeys ? ['a', 'b'] : ['a', 'b', 'c'],
+ actual = func(new Foo);
- var expected = ['a', 'b'];
- deepEqual(_.keys(Foo), expected);
-
- Foo.prototype = { 'c': 3 };
- deepEqual(_.keys(Foo), expected);
+ deepEqual(actual.sort(), expected);
});
- }(1, 2, 3));
+ });
/*--------------------------------------------------------------------------*/
@@ -4947,21 +5595,33 @@
];
test('should return the last element', 1, function() {
- equal(_.last(array), 3);
+ strictEqual(_.last(array), 3);
});
test('should return the last two elements', 1, function() {
deepEqual(_.last(array, 2), [2, 3]);
});
+ test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value == null ? 3 : [];
+ });
+
+ var actual = _.map(falsey, function(n) {
+ return _.last(array, n);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should return an empty array when `n` < `1`', 3, function() {
- _.forEach([0, -1, -2], function(n) {
+ _.each([0, -1, -Infinity], function(n) {
deepEqual(_.last(array, n), []);
});
});
- test('should return all elements when `n` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(n) {
+ test('should return all elements when `n` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.last(array, n), array);
});
});
@@ -5029,7 +5689,7 @@
test('should not chain when arguments are not provided', 1, function() {
if (!isNpm) {
var actual = _(array).last();
- equal(actual, 3);
+ strictEqual(actual, 3);
}
else {
skipTest();
@@ -5053,37 +5713,50 @@
var array = [1, 2, 3, 1, 2, 3];
test('should return the index of the last matched value', 1, function() {
- equal(_.lastIndexOf(array, 3), 5);
+ strictEqual(_.lastIndexOf(array, 3), 5);
});
test('should return `-1` for an unmatched value', 1, function() {
- equal(_.lastIndexOf(array, 4), -1);
+ strictEqual(_.lastIndexOf(array, 4), -1);
});
test('should work with a positive `fromIndex`', 1, function() {
strictEqual(_.lastIndexOf(array, 1, 2), 0);
});
- test('should work with `fromIndex` >= `array.length`', 6, function() {
- _.forEach([6, 8], function(fromIndex) {
- equal(_.lastIndexOf(array, undefined, fromIndex), -1);
- equal(_.lastIndexOf(array, 1, fromIndex), 3);
- equal(_.lastIndexOf(array, '', fromIndex), -1);
+ test('should work with `fromIndex` >= `array.length`', 12, function() {
+ _.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
+ strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1);
+ strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
+ strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
});
});
+ 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;
+ });
+
+ var actual = _.map(falsey, function(fromIndex) {
+ return _.lastIndexOf(array, 3, fromIndex);
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should treat non-number `fromIndex` values as `array.length`', 2, function() {
+ strictEqual(_.lastIndexOf(array, 3, '1'), 5);
+ strictEqual(_.lastIndexOf(array, 3, true), 5);
+ });
+
test('should work with a negative `fromIndex`', 1, function() {
strictEqual(_.lastIndexOf(array, 2, -3), 1);
});
- test('should work with a negative `fromIndex` <= `-array.length`', 2, function() {
- strictEqual(_.lastIndexOf(array, 1, -6), 0);
- equal(_.lastIndexOf(array, 2, -8), -1);
- });
-
- test('should ignore non-number `fromIndex` values', 2, function() {
- equal(_.lastIndexOf([1, 2, 3], 3, '1'), 2);
- equal(_.lastIndexOf([1, 2, 3], 3, true), 2);
+ test('should work with a negative `fromIndex` <= `-array.length`', 3, function() {
+ _.each([-6, -8, -Infinity], function(fromIndex) {
+ strictEqual(_.lastIndexOf(array, 1, fromIndex), 0);
+ });
});
}());
@@ -5092,11 +5765,11 @@
QUnit.module('indexOf methods');
(function() {
- _.forEach(['indexOf', 'lastIndexOf'], function(methodName) {
+ _.each(['indexOf', 'lastIndexOf'], function(methodName) {
var func = _[methodName];
test('`_.' + methodName + '` should accept a falsey `array` argument', 1, function() {
- var expected = _.map(falsey, function() { return -1; });
+ var expected = _.map(falsey, _.constant(-1));
var actual = _.map(falsey, function(value, index) {
try {
@@ -5163,7 +5836,7 @@
test('should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
- ok(_(array).map(noop) instanceof _);
+ ok(_(array).map(_.noop) instanceof _);
}
else {
skipTest();
@@ -5183,8 +5856,8 @@
}
});
- test('should accept a falsey `array` argument', 1, function() {
- var expected = _.map(falsey, function() { return []; });
+ test('should accept a falsey `collection` argument', 1, function() {
+ var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
@@ -5195,6 +5868,10 @@
deepEqual(actual, expected);
});
+ test('should treat number values for `collection` as empty', 1, function() {
+ deepEqual(_.map(1), []);
+ });
+
test('should be aliased', 1, function() {
strictEqual(_.collect, _.map);
});
@@ -5244,7 +5921,7 @@
test('should return a wrapped value when chaining', 1, function() {
if (!isNpm) {
- ok(_(object).mapValues(noop) instanceof _);
+ ok(_(object).mapValues(_.noop) instanceof _);
}
else {
skipTest();
@@ -5252,7 +5929,7 @@
});
test('should accept a falsey `object` argument', 1, function() {
- var expected = _.map(falsey, function() { return {}; });
+ var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
try {
@@ -5269,21 +5946,60 @@
QUnit.module('lodash.matches');
(function() {
- var object = { 'a': 1, 'b': 2 };
+ var object = { 'a': 1, 'b': 2, 'c': 3 },
+ sources = [{ 'a': 1 }, { 'a': 1, 'c': 3 }];
- test('should create a function that performs a deep comparison between a given object and the `source` object', 3, function() {
- var matches = _.matches({ 'a': 1 });
+ test('should create a function that performs a deep comparison between a given object and the `source` object', 6, function() {
+ _.each(sources, function(source, index) {
+ var matches = _.matches(source);
+ strictEqual(matches.length, 1);
+ strictEqual(matches(object), true);
- equal(matches.length, 1);
- strictEqual(matches(object), true);
-
- matches = _.matches({ 'b': 1 });
- strictEqual(matches(object), false);
+ matches = _.matches(index ? { 'c': 3, 'd': 4 } : { 'b': 1 });
+ strictEqual(matches(object), false);
+ });
});
- test('should return `false` when comparing an empty `source`', 1, function() {
- var matches = _.matches({});
- strictEqual(matches(object), false);
+ test('should return `true` when comparing an empty `source`', 1, function() {
+ var expected = _.map(empties, _.constant(true));
+
+ var actual = _.map(empties, function(value) {
+ var matches = _.matches(value);
+ return matches(object) === true;
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should not error error for falsey `object` values', 2, function() {
+ var expected = _.map(falsey, _.constant(true));
+
+ _.each(sources, function(source) {
+ var matches = _.matches(source);
+
+ var actual = _.map(falsey, function(value, index) {
+ try {
+ var result = index ? matches(value) : matches();
+ return result === false;
+ } catch(e) { }
+ });
+
+ deepEqual(actual, expected);
+ });
+ });
+
+ test('should return `true` when comparing an empty `source` to a falsey `object`', 1, function() {
+ var expected = _.map(falsey, _.constant(true)),
+ matches = _.matches({});
+
+ var actual = _.map(falsey, function(value, index) {
+ try {
+ var result = index ? matches(value) : matches();
+ return result === true;
+ } catch(e) { }
+ });
+
+ deepEqual(actual, expected);
});
}());
@@ -5293,7 +6009,7 @@
(function() {
test('should return the largest value from a collection', 1, function() {
- equal(3, _.max([1, 2, 3]));
+ strictEqual(3, _.max([1, 2, 3]));
});
test('should return `-Infinity` for empty collections', 1, function() {
@@ -5332,16 +6048,16 @@
return a + b + c;
});
- equal(memoized(1, 2, 3), 6);
- equal(memoized(1, 3, 5), 6);
+ strictEqual(memoized(1, 2, 3), 6);
+ strictEqual(memoized(1, 3, 5), 6);
});
test('should support a `resolver` argument', 2, function() {
var fn = function(a, b, c) { return a + b + c; },
memoized = _.memoize(fn, fn);
- equal(memoized(1, 2, 3), 6);
- equal(memoized(1, 3, 5), 9);
+ strictEqual(memoized(1, 2, 3), 6);
+ strictEqual(memoized(1, 3, 5), 9);
});
test('should not set a `this` binding', 2, function() {
@@ -5350,15 +6066,31 @@
});
var object = { 'b': 2, 'c': 3, 'memoized': memoized };
- equal(object.memoized(1), 6);
- equal(object.memoized(2), 7);
+ strictEqual(object.memoized(1), 6);
+ strictEqual(object.memoized(2), 7);
+ });
+
+ test('should throw a TypeError if `resolve` is truthy and not a function', function() {
+ raises(function() { _.memoize(_.noop, {}); }, TypeError);
+ });
+
+ test('should not throw a TypeError if `resolve` is falsey', function() {
+ var expected = _.map(falsey, _.constant(true));
+
+ var actual = _.map(falsey, function(value, index) {
+ try {
+ return _.isFunction(index ? _.memoize(_.noop, value) : _.memoize(_.noop));
+ } catch(e) { }
+ });
+
+ deepEqual(actual, expected);
});
test('should check cache for own properties', 1, function() {
var actual = [],
memoized = _.memoize(_.identity);
- _.forEach(shadowedProps, function(value) {
+ _.each(shadowedProps, function(value) {
actual.push(memoized(value));
});
@@ -5366,14 +6098,14 @@
});
test('should expose a `cache` object on the `memoized` function', 4, function() {
- _.forEach(['_a', 'a'], function(key, index) {
+ _.each(['_a', 'a'], function(key, index) {
var memoized = _.memoize(_.identity, index && _.identity);
memoized('a');
- equal(memoized.cache[key], 'a');
+ strictEqual(memoized.cache[key], 'a');
memoized.cache[key] = 'b';
- equal(memoized('a'), 'b');
+ strictEqual(memoized('a'), 'b');
});
});
}());
@@ -5445,7 +6177,7 @@
};
var actual = _.merge(object, source);
- equal(_.isArguments(actual.args), false);
+ strictEqual(_.isArguments(actual.args), false);
});
test('should work with four arguments', 1, function() {
@@ -5494,7 +6226,7 @@
(function() {
test('should return the smallest value from a collection', 1, function() {
- equal(1, _.min([1, 2, 3]));
+ strictEqual(1, _.min([1, 2, 3]));
});
test('should return `Infinity` for empty collections', 1, function() {
@@ -5527,15 +6259,16 @@
QUnit.module('lodash.max and lodash.min');
- _.forEach(['max', 'min'], function(methodName) {
+ _.each(['max', 'min'], function(methodName) {
var array = [1, 2, 3],
- func = _[methodName];
+ func = _[methodName],
+ isMax = methodName == 'max';
test('`_.' + methodName + '` should work with Date objects', 1, function() {
var now = new Date,
past = new Date(0);
- equal(func([now, past]), methodName == 'max' ? now : past);
+ strictEqual(func([now, past]), isMax ? now : past);
});
test('`_.' + methodName + '` should work with a callback argument', 1, function() {
@@ -5543,7 +6276,7 @@
return -num;
});
- equal(actual, methodName == 'max' ? 1 : 3);
+ strictEqual(actual, isMax ? 1 : 3);
});
test('`_.' + methodName + '` should pass the correct `callback` arguments when iterating an array', 1, function() {
@@ -5577,29 +6310,39 @@
return -this[index];
}, array);
- equal(actual, methodName == 'max' ? 1 : 3);
+ strictEqual(actual, isMax ? 1 : 3);
});
test('`_.' + methodName + '` should work when used as a callback for `_.map`', 1, function() {
var array = [[2, 3, 1], [5, 6, 4], [8, 9, 7]],
actual = _.map(array, func);
- deepEqual(actual, methodName == 'max' ? [3, 6, 9] : [1, 4, 7]);
+ deepEqual(actual, isMax ? [3, 6, 9] : [1, 4, 7]);
});
test('`_.' + methodName + '` should iterate an object', 1, function() {
var actual = func({ 'a': 1, 'b': 2, 'c': 3 });
- equal(actual, methodName == 'max' ? 3 : 1);
+ strictEqual(actual, isMax ? 3 : 1);
});
test('`_.' + methodName + '` should iterate a string', 2, function() {
- _.forEach(['abc', Object('abc')], function(value) {
+ _.each(['abc', Object('abc')], function(value) {
var actual = func(value);
- equal(actual, methodName == 'max' ? 'c' : 'a');
+ strictEqual(actual, isMax ? 'c' : 'a');
});
});
- test('`_.' + methodName + '` should resolve the correct value when provided an array containing only one value', 1, function() {
+ test('`_.' + methodName + '` should work when `callback` returns +/-Infinity', 1, function() {
+ var object = { 'a': (isMax ? -Infinity : Infinity) };
+
+ var actual = func([object, { 'a': object.a }], function(object) {
+ return object.a;
+ });
+
+ strictEqual(actual, object);
+ });
+
+ test('`_.' + methodName + '` should work when chaining on an array with only one value', 1, function() {
if (!isNpm) {
var actual = _([40])[methodName]().value();
strictEqual(actual, 40);
@@ -5611,7 +6354,7 @@
test('`_.' + methodName + '` should work with extremely large arrays', 1, function() {
var array = _.range(0, 5e5);
- equal(func(array), methodName == 'max' ? 499999 : 0);
+ strictEqual(func(array), isMax ? 499999 : 0);
});
});
@@ -5628,15 +6371,36 @@
}
var value = ['a'],
- source = { 'a': function(array) { return array[0]; } };
+ source = { 'a': function(array) { return array[0]; }, 'b': 'B' };
- test('should accept an `object` argument', 1, function() {
- var lodash = {};
- _.mixin(lodash, source);
- strictEqual(lodash.a(value), 'a');
+ test('should use `this` as the default `object` value', 3, function() {
+ var object = _.create(_);
+ object.mixin(source);
+
+ strictEqual(object.a(value), 'a');
+
+ ok(!('a' in _));
+ ok(!('a' in _.prototype));
+
+ delete wrapper.a;
+ delete wrapper.prototype.a;
+ delete wrapper.b;
+ delete wrapper.prototype.b;
});
- test('should accept a function `object` argument', 2, function() {
+ test('should accept an `object` argument', 1, function() {
+ var object = {};
+ _.mixin(object, source);
+ strictEqual(object.a(value), 'a');
+ });
+
+ test('should return `object`', 2, function() {
+ var object = {};
+ strictEqual(_.mixin(object, source), object);
+ strictEqual(_.mixin(), _);
+ });
+
+ test('should work with a function for `object`', 2, function() {
_.mixin(wrapper, source);
var wrapped = wrapper(value),
@@ -5647,30 +6411,24 @@
delete wrapper.a;
delete wrapper.prototype.a;
+ delete wrapper.b;
+ delete wrapper.prototype.b;
});
test('should mixin `source` methods into lodash', 4, function() {
- if (!isNpm) {
- _.mixin({
- 'a': 'a',
- 'A': function(string) { return string.toUpperCase(); }
- });
+ _.mixin(source);
- equal('a' in _, false);
- equal('a' in _.prototype, false);
+ strictEqual(_.a(value), 'a');
+ strictEqual(_(value).a().__wrapped__, 'a');
- delete _.a;
- delete _.prototype.a;
+ delete _.a;
+ delete _.prototype.a;
- equal(_.A('a'), 'A');
- equal(_('a').A().value(), 'A');
+ ok(!('b' in _));
+ ok(!('b' in _.prototype));
- delete _.A;
- delete _.prototype.A;
- }
- else {
- skipTest(4);
- }
+ delete _.b;
+ delete _.prototype.b;
});
test('should accept an `options` argument', 16, function() {
@@ -5678,8 +6436,8 @@
return (func === _ ? 'lodash' : 'provided') + ' function should ' + (chain ? '' : 'not ') + 'chain';
}
- _.forEach([_, wrapper], function(func) {
- _.forEach([false, true, { 'chain': false }, { 'chain': true }], function(options) {
+ _.each([_, wrapper], function(func) {
+ _.each([false, true, { 'chain': false }, { 'chain': true }], function(options) {
if (func === _) {
_.mixin(source, options);
} else {
@@ -5688,15 +6446,17 @@
var wrapped = func(value),
actual = wrapped.a();
- if (options && (options === true || options.chain)) {
+ if (options === true || (options && options.chain)) {
strictEqual(actual.__wrapped__, 'a', message(func, true));
ok(actual instanceof func, message(func, true));
} else {
strictEqual(actual, 'a', message(func, false));
- equal(actual instanceof func, false, message(func, false));
+ ok(!(actual instanceof func), message(func, false));
}
delete func.a;
delete func.prototype.a;
+ delete func.b;
+ delete func.prototype.b;
});
});
});
@@ -5720,9 +6480,36 @@
}
delete _.a;
delete _.prototype.a;
+ delete _.b;
+ delete _.prototype.b;
ok(pass);
});
+
+ test('should return the existing wrapper when chaining', 2, function() {
+ if (!isNpm) {
+ _.each([_, wrapper], function(func) {
+ if (func === _) {
+ var wrapper = _(source),
+ actual = wrapper.mixin();
+
+ strictEqual(actual.value(), _);
+ }
+ else {
+ wrapper = _(func);
+ actual = wrapper.mixin(source);
+ strictEqual(actual, wrapper);
+ }
+ delete func.a;
+ delete func.prototype.a;
+ delete func.b;
+ delete func.prototype.b;
+ });
+ }
+ else {
+ skipTest(2);
+ }
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -5732,7 +6519,7 @@
(function() {
test('should always return `undefined`', 1, function() {
var values = falsey.concat([], true, new Date, _, {}, /x/, 'a'),
- expected = _.map(values, function() { return undefined; });
+ expected = _.map(values, _.constant());
var actual = _.map(values, function(value, index) {
return index ? _.noop(value) : _.noop();
@@ -5847,16 +6634,16 @@
(function() {
test('should execute `func` once', 1, function() {
var count = 0,
- func = _.once(function() { count++; });
+ once = _.once(function() { count++; });
- func();
- func();
+ once();
+ once();
strictEqual(count, 1);
});
test('should not set a `this` binding', 1, function() {
- var func = _.once(function() { this.count++; }),
- object = { 'count': 0, 'once': func };
+ var once = _.once(function() { this.count++; }),
+ object = { 'count': 0, 'once': once };
object.once();
object.once();
@@ -5866,26 +6653,26 @@
test('should ignore recursive calls', 1, function() {
var count = 0;
- var func = _.once(function() {
+ var once = _.once(function() {
count++;
- func();
+ once();
});
- func();
+ once();
strictEqual(count, 1);
});
test('should not throw more than once', 2, function() {
- var pass = true;
-
- var func = _.once(function() {
+ var once = _.once(function() {
throw new Error;
});
- raises(function() { func(); }, Error);
+ raises(function() { once(); }, Error);
+
+ var pass = true;
try {
- func();
+ once();
} catch(e) {
pass = false;
}
@@ -5895,6 +6682,104 @@
/*--------------------------------------------------------------------------*/
+ QUnit.module('lodash.pad');
+
+ (function() {
+ test('should pad a string to a given length', 1, function() {
+ strictEqual(_.pad('abc', 9), ' abc ');
+ });
+
+ test('should truncate pad characters to fit the pad length', 2, function() {
+ strictEqual(_.pad('abc', 8), ' abc ');
+ strictEqual(_.pad('abc', 8, '_-'), '_-abc_-_');
+ });
+
+ test('should coerce `string` to a string', 2, function() {
+ strictEqual(_.pad(Object('abc'), 4), 'abc ');
+ strictEqual(_.pad({ 'toString': _.constant('abc') }, 5), ' abc ');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.padLeft');
+
+ (function() {
+ test('should pad a string to a given length', 1, function() {
+ strictEqual(_.padLeft('abc', 6), ' abc');
+ });
+
+ test('should truncate pad characters to fit the pad length', 1, function() {
+ strictEqual(_.padLeft('abc', 6, '_-'), '_-_abc');
+ });
+
+ test('should coerce `string` to a string', 2, function() {
+ strictEqual(_.padLeft(Object('abc'), 4), ' abc');
+ strictEqual(_.padLeft({ 'toString': _.constant('abc') }, 5), ' abc');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.padRight');
+
+ (function() {
+ test('should pad a string to a given length', 1, function() {
+ strictEqual(_.padRight('abc', 6), 'abc ');
+ });
+
+ test('should truncate pad characters to fit the pad length', 1, function() {
+ strictEqual(_.padRight('abc', 6, '_-'), 'abc_-_');
+ });
+
+ test('should coerce `string` to a string', 2, function() {
+ strictEqual(_.padRight(Object('abc'), 4), 'abc ');
+ strictEqual(_.padRight({ 'toString': _.constant('abc') }, 5), 'abc ');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('pad methods');
+
+ _.each(['pad', 'padLeft', 'padRight'], function(methodName, index) {
+ var func = _[methodName];
+
+ test('`_.' + methodName + '` should not pad is string is >= `length`', 2, function() {
+ strictEqual(func('abc', 2), 'abc');
+ strictEqual(func('abc', 3), 'abc');
+ });
+
+ test('`_.' + methodName + '` should treat negative `length` as `0`', 2, function() {
+ _.each([0, -2], function(length) {
+ strictEqual(func('abc', length), 'abc');
+ });
+ });
+
+ test('`_.' + methodName + '` should coerce `length` to a number', 2, function() {
+ _.each(['', '4'], function(length) {
+ var actual = length ? (index == 1 ? ' abc' : 'abc ') : 'abc';
+ strictEqual(func('abc', length), actual);
+ });
+ });
+
+ test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string and `chars`', 6, function() {
+ _.each([null, '_-'], function(chars) {
+ strictEqual(func(null, 0, chars), '');
+ strictEqual(func(undefined, 0, chars), '');
+ strictEqual(func('', 0, chars), '');
+ });
+ });
+
+ test('`_.' + methodName + '` should work with `null`, `undefined`, or empty string for `chars`', 3, function() {
+ notStrictEqual(func('abc', 6, null), 'abc');
+ notStrictEqual(func('abc', 6, undefined), 'abc');
+ strictEqual(func('abc', 6, ''), 'abc');
+ });
+ });
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash.pairs');
(function() {
@@ -5932,7 +6817,7 @@
});
test('should use a radix of `16`, for hexadecimals, if `radix` is `undefined` or `0`', 8, function() {
- _.forEach(['0x20', '0X20'], function(string) {
+ _.each(['0x20', '0X20'], function(string) {
strictEqual(_.parseInt(string), 32);
strictEqual(_.parseInt(string, 0), 32);
strictEqual(_.parseInt(string, 16), 32);
@@ -5952,7 +6837,7 @@
strictEqual(_.parseInt(whitespace + '08'), 8);
strictEqual(_.parseInt(whitespace + '08', 10), 8);
- _.forEach(['0x20', '0X20'], function(string) {
+ _.each(['0x20', '0X20'], function(string) {
strictEqual(_.parseInt(whitespace + string), 32);
strictEqual(_.parseInt(whitespace + string, 16), 32);
});
@@ -5969,15 +6854,13 @@
QUnit.module('partial methods');
- _.forEach(['partial', 'partialRight'], function(methodName) {
+ _.each(['partial', 'partialRight'], function(methodName) {
var func = _[methodName],
isPartial = methodName == 'partial';
test('`_.' + methodName + '` partially applies arguments', 1, function() {
- var fn = function(a) { return a; },
- par = func(fn, 'a');
-
- equal(par(), 'a');
+ var par = func(_.identity, 'a');
+ strictEqual(par(), 'a');
});
test('`_.' + methodName + '` creates a function that can be invoked with additional arguments', 1, function() {
@@ -5996,10 +6879,29 @@
});
test('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked with additional arguments', 1, function() {
- var fn = function(a) { return a; },
- par = func(fn);
+ var par = func(_.identity);
+ strictEqual(par('a'), 'a');
+ });
- equal(par('a'), 'a');
+ test('`_.' + methodName + '` should support placeholders', 4, function() {
+ if (!isModularize) {
+ var fn = function() { return slice.call(arguments); },
+ par = func(fn, _, 'b', _);
+
+ deepEqual(par('a', 'c'), ['a', 'b', 'c']);
+ deepEqual(par('a'), ['a', 'b', undefined]);
+ deepEqual(par(), [undefined, 'b', undefined]);
+
+ if (isPartial) {
+ deepEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']);
+ } else {
+ par = func(fn, _, 'c', _);
+ deepEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']);
+ }
+ }
+ else {
+ skipTest(4);
+ }
});
test('`_.' + methodName + '` should not alter the `this` binding', 3, function() {
@@ -6027,36 +6929,14 @@
function Foo(value) {
return value && object;
}
- var par = func(Foo),
- object = {};
+
+ var object = {},
+ par = func(Foo);
ok(new par instanceof Foo);
strictEqual(new par(true), object);
});
- test('`_.' + methodName + '` should support placeholders', 4, function() {
- if (_._iteratorTemplate) {
- var fn = function() {
- return slice.call(arguments);
- };
-
- var par = func(fn, _, 'b', _);
- deepEqual(par('a', 'c'), ['a', 'b', 'c']);
- deepEqual(par('a'), ['a', 'b', undefined]);
- deepEqual(par(), [undefined, 'b', undefined]);
-
- if (isPartial) {
- deepEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']);
- } else {
- par = func(fn, _, 'c', _);
- deepEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']);
- }
- }
- else {
- skipTest(4);
- }
- });
-
test('`_.' + methodName + '` should clone metadata for created functions', 3, function() {
var greet = function(greeting, name) {
return greeting + ' ' + name;
@@ -6066,17 +6946,17 @@
par2 = func(par1, 'barney'),
par3 = func(par1, 'pebbles');
- equal(par1('fred'), isPartial ? 'hi fred' : 'fred hi')
- equal(par2(), isPartial ? 'hi barney' : 'barney hi');
- equal(par3(), isPartial ? 'hi pebbles' : 'pebbles hi');
+ strictEqual(par1('fred'), isPartial ? 'hi fred' : 'fred hi')
+ strictEqual(par2(), isPartial ? 'hi barney' : 'barney hi');
+ strictEqual(par3(), isPartial ? 'hi pebbles' : 'pebbles hi');
});
test('`_.' + methodName + '` should work with curried methods', 2, function() {
var fn = function(a, b, c) { return a + b + c; },
curried = _.curry(func(fn, 1), 2);
- equal(curried(2, 3), 6);
- equal(curried(2)(3), 6);
+ strictEqual(curried(2, 3), 6);
+ strictEqual(curried(2)(3), 6);
});
});
@@ -6101,9 +6981,9 @@
(function() {
test('combinations of partial functions should work', 1, function() {
- var fn = function() {
+ function fn() {
return slice.call(arguments);
- };
+ }
var a = _.partial(fn),
b = _.partialRight(a, 3),
@@ -6113,11 +6993,11 @@
});
test('combinations of bound and partial functions should work', 3, function() {
- var fn = function() {
+ function fn() {
var result = [this.a];
push.apply(result, arguments);
return result;
- };
+ }
var expected = [1, 2, 3, 4],
object = { 'a': 1, 'fn': fn };
@@ -6142,9 +7022,9 @@
});
test('recursively bound functions should work', 1, function() {
- var fn = function() {
+ function fn() {
return this.a;
- };
+ }
var a = _.bind(fn, { 'a': 1 }),
b = _.bind(a, { 'a': 2 }),
@@ -6162,9 +7042,9 @@
var array = [1, 0, 1];
test('should always return two groups of elements', 3, function() {
- deepEqual(_.partition([], function(value) { return value; }), [[], []]);
- deepEqual(_.partition(array, function(value) { return true; }), [array, []]);
- deepEqual(_.partition(array, function(value) { return false; }), [[], array]);
+ deepEqual(_.partition([], _.identity), [[], []]);
+ deepEqual(_.partition(array, _.constant(true)), [array, []]);
+ deepEqual(_.partition(array, _.constant(false)), [[], array]);
});
test('should use `_.identity` when no `callback` is provided', 1, function() {
@@ -6307,6 +7187,25 @@
var object = { 'a': [1], 'b': [1, 2], 'c': [1, 2, 3] };
deepEqual(_.pluck(object, 'length'), [1, 2, 3]);
});
+
+ test('should work with nullish elements', 1, function() {
+ var objects = [{ 'a': 1 }, null, undefined, { 'a': 4 }];
+ deepEqual(_.pluck(objects, 'a'), [1, undefined, undefined, 4]);
+ });
+
+ test('should coerce `key` to a string', 1, function() {
+ function fn() {}
+ fn.toString = _.constant('fn');
+
+ var objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
+ values = [null, undefined, fn, {}]
+
+ var actual = _.map(objects, function(object, index) {
+ return _.pluck([object], values[index]);
+ });
+
+ deepEqual(actual, [[1], [2], [3], [4]]);
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -6318,10 +7217,10 @@
var object = { 'a': 1, 'b': 2 },
property = _.property('a');
- equal(property.length, 1);
+ strictEqual(property.length, 1);
strictEqual(property(object), 1);
- property = _.property('b');
+ property = _.property('b');
strictEqual(property(object), 2);
});
@@ -6329,7 +7228,7 @@
var array = [1, 2, 3],
property = _.property(1);
- equal(property(array), 2);
+ strictEqual(property(array), 2);
});
}());
@@ -6352,8 +7251,8 @@
delete array[3];
_.pull(array, 1);
- equal(0 in array, false);
- equal(2 in array, false);
+ ok(!('0' in array));
+ ok(!('2' in array));
});
test('should treat holes as `undefined`', 1, function() {
@@ -6367,6 +7266,60 @@
/*--------------------------------------------------------------------------*/
+ QUnit.module('lodash.pullAt');
+
+ (function() {
+ test('should modify the array and return removed elements', 2, function() {
+ var array = [1, 2, 3],
+ actual = _.pullAt(array, [0, 1]);
+
+ deepEqual(array, [3]);
+ deepEqual(actual, [1, 2]);
+ });
+
+ test('should work with unsorted indexes', 2, function() {
+ var array = [1, 2, 3, 4, 5],
+ actual = _.pullAt(array, [4, 1, 0, 3]);
+
+ deepEqual(array, [3]);
+ deepEqual(actual, [5, 2, 1, 4]);
+ });
+
+ test('should work with repeated indexes', 2, function() {
+ var array = [1, 2, 3, 4],
+ actual = _.pullAt(array, [0, 2, 0, 1, 0, 2]);
+
+ deepEqual(array, [4]);
+ deepEqual(actual, [1, 3, 1, 2, 1, 3]);
+ });
+
+ test('should return `undefined` for nonexistent keys', 2, function() {
+ var array = ['a', 'b', 'c'],
+ actual = _.pullAt(array, [2, 4, 0]);
+
+ deepEqual(array, ['b']);
+ deepEqual(actual, ['c', undefined, 'a']);
+ });
+
+ test('should return an empty array when no keys are provided', 2, function() {
+ var array = ['a', 'b', 'c'],
+ actual = _.pullAt(array);
+
+ deepEqual(array, ['a', 'b', 'c']);
+ deepEqual(actual, []);
+ });
+
+ test('should accept multiple index arguments', 2, function() {
+ var array = ['a', 'b', 'c', 'd'],
+ actual = _.pullAt(array, 3, 0, 2);
+
+ deepEqual(array, ['b']);
+ deepEqual(actual, ['d', 'a', 'c']);
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash.random');
(function() {
@@ -6382,7 +7335,7 @@
test('supports not passing a `max` argument', 1, function() {
ok(_.some(array, function() {
- return _.random(5) != 5;
+ return _.random(5) !== 5;
}));
});
@@ -6429,8 +7382,6 @@
QUnit.module('lodash.range');
(function() {
- var func = _.range;
-
test('should work when passing a single `end` argument', 1, function() {
deepEqual(_.range(4), [0, 1, 2, 3]);
});
@@ -6457,7 +7408,7 @@
});
test('should treat falsey `start` arguments as `0`', 13, function() {
- _.forEach(falsey, function(value, index) {
+ _.each(falsey, function(value, index) {
if (index) {
deepEqual(_.range(value), []);
deepEqual(_.range(value, 1), [0]);
@@ -6467,9 +7418,9 @@
});
});
- test('should coerce arguments to numbers', 1, function() {
- var actual = [func('0',1), func('1'), func(0, 1, '1')];
- deepEqual(actual, [[0], [0], [0]]);
+ test('should coerce arguments to finite numbers', 1, function() {
+ var actual = [_.range('0', 1), _.range('1'), _.range(0, 1, '1'), _.range(NaN), _.range(NaN, NaN)];
+ deepEqual(actual, [[0], [0], [0], [], []]);
});
}());
@@ -6528,7 +7479,7 @@
deepEqual(args, expected);
});
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -6542,7 +7493,7 @@
});
deepEqual(args, ['a', 'b', 1, collection]);
- equal(actual, 'abc');
+ strictEqual(actual, 'abc');
});
});
@@ -6560,7 +7511,7 @@
var array = [1, 2, 3];
test('should use the last element of a collection as the default `accumulator`', 1, function() {
- equal(_.reduceRight(array), 3);
+ strictEqual(_.reduceRight(array), 3);
});
test('should pass the correct `callback` arguments when iterating an array', 2, function() {
@@ -6607,7 +7558,7 @@
deepEqual(args, expected);
});
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -6621,7 +7572,7 @@
});
deepEqual(args, ['c', 'b', 1, collection]);
- equal(actual, 'cba');
+ strictEqual(actual, 'cba');
});
});
@@ -6634,7 +7585,7 @@
QUnit.module('reduce methods');
- _.forEach(['reduce', 'reduceRight'], function(methodName) {
+ _.each(['reduce', 'reduceRight'], function(methodName) {
var array = [1, 2, 3],
func = _[methodName];
@@ -6643,7 +7594,7 @@
return accumulator + value;
}, '');
- equal(actual, methodName == 'reduce' ? 'abc' : 'cba');
+ strictEqual(actual, methodName == 'reduce' ? 'abc' : 'cba');
});
test('`_.' + methodName + '` should support the `thisArg` argument', 1, function() {
@@ -6660,7 +7611,7 @@
return sum + num;
});
- equal(actual, 6);
+ strictEqual(actual, 6);
}
else {
skipTest();
@@ -6669,11 +7620,11 @@
test('`_.' + methodName + '` should support empty or falsey collections without an initial `accumulator` value', 1, function() {
var actual = [],
- expected = _.map(empties, function() { return undefined; });
+ expected = _.map(empties, _.constant());
- _.forEach(empties, function(value) {
+ _.each(empties, function(value) {
try {
- actual.push(func(value, noop));
+ actual.push(func(value, _.noop));
} catch(e) { }
});
@@ -6681,11 +7632,11 @@
});
test('`_.' + methodName + '` should support empty or falsey collections with an initial `accumulator` value', 1, function() {
- var expected = _.map(empties, function() { return 'x'; });
+ var expected = _.map(empties, _.constant('x'));
var actual = _.map(empties, function(value) {
try {
- return func(value, noop, 'x');
+ return func(value, _.noop, 'x');
} catch(e) { }
});
@@ -6693,7 +7644,7 @@
});
test('`_.' + methodName + '` should handle an initial `accumulator` value of `undefined`', 1, function() {
- var actual = func([], noop, undefined);
+ var actual = func([], _.noop, undefined);
strictEqual(actual, undefined);
});
@@ -6703,12 +7654,12 @@
if ('__proto__' in array) {
array.__proto__ = object;
- strictEqual(_.reduce(array, noop), undefined);
+ strictEqual(_.reduce(array, _.noop), undefined);
}
else {
skipTest();
}
- strictEqual(_.reduce(object, noop), undefined);
+ strictEqual(_.reduce(object, _.noop), undefined);
});
});
@@ -6730,7 +7681,7 @@
QUnit.module('filter methods');
- _.forEach(['filter', 'reject'], function(methodNames) {
+ _.each(['filter', 'reject'], function(methodNames) {
var func = _[methodNames];
test('`_.' + methodNames + '` should not modify the resulting value from within `callback`', 1, function() {
@@ -6786,8 +7737,8 @@
delete array[3];
_.remove(array, function(num) { return num === 1; });
- equal(0 in array, false);
- equal(2 in array, false);
+ ok(!('0' in array));
+ ok(!('2' in array));
});
test('should treat holes as `undefined`', 1, function() {
@@ -6801,63 +7752,28 @@
/*--------------------------------------------------------------------------*/
- QUnit.module('lodash.removeAt');
+ QUnit.module('lodash.repeat');
(function() {
- test('should modify the array and return removed elements', 2, function() {
- var array = [1, 2, 3];
- var actual = _.removeAt(array, [0, 1]);
-
- deepEqual(array, [3]);
- deepEqual(actual, [1, 2]);
+ test('should repeat a string `n` times', 2, function() {
+ strictEqual(_.repeat('*', 3), '***');
+ strictEqual(_.repeat('abc', 2), 'abcabc');
});
- test('should work with unsorted indexes', 2, function() {
- var array = [1, 2, 3, 4, 5];
- var actual = _.removeAt(array, [4, 1, 0, 3]);
-
- deepEqual(array, [3]);
- deepEqual(actual, [1, 2, 4, 5]);
+ test('should return an empty string for negative `n` or `n` of `0`', 2, function() {
+ strictEqual(_.repeat('abc', 0), '');
+ strictEqual(_.repeat('abc', -2), '');
});
- test('should work with repeated indexes', 2, function() {
- var array = [1, 2, 3, 4, 5];
- var actual = _.removeAt(array, [0, 0, 1, 2, 2, 2]);
-
- deepEqual(array, [4, 5]);
- deepEqual(actual, [1, 1, 2, 3, 3, 3]);
+ test('should coerce `n` to a number', 3, function() {
+ strictEqual(_.repeat('abc'), '');
+ strictEqual(_.repeat('abc', '2'), 'abcabc');
+ strictEqual(_.repeat('*', { 'valueOf': _.constant(3) }), '***');
});
- test('should return `undefined` for nonexistent keys', 2, function() {
- var array = ['a', 'b', 'c'];
- var actual = _.removeAt(array, [0, 2, 4]);
-
- deepEqual(array, ['b']);
- deepEqual(actual, ['a', 'c', undefined]);
- });
-
- test('should return an empty array when no keys are provided', 2, function() {
- var array = ['a', 'b', 'c'];
- var actual = _.removeAt(array);
-
- deepEqual(array, ['a', 'b', 'c']);
- deepEqual(actual, []);
- });
-
- test('should accept multiple index arguments', 2, function() {
- var array = ['a', 'b', 'c', 'd'];
- var actual = _.removeAt(array, 0, 2, 3);
-
- deepEqual(array, ['b']);
- deepEqual(actual, ['a', 'c', 'd']);
- });
-
- test('should work when used as a callback for `_.map`', 2, function() {
- var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]];
- var actual = _.map(array, _.removeAt);
-
- deepEqual(array, [[2, 3], [4, 6], [7, 8]]);
- deepEqual(actual, [[1], [5], [9]]);
+ test('should coerce `string` to a string', 2, function() {
+ strictEqual(_.repeat(Object('abc'), 2), 'abcabc');
+ strictEqual(_.repeat({ 'toString': _.constant('*') }, 3), '***');
});
}());
@@ -6879,13 +7795,13 @@
strictEqual(_.result(object, 'd'), undefined);
});
- test('should return `undefined` when `object` is `null` or `undefined`', 2, function() {
+ test('should return `undefined` when `object` is nullish', 2, function() {
strictEqual(_.result(null, 'a'), undefined);
strictEqual(_.result(undefined, 'a'), undefined);
});
test('should return the specified default value for undefined properties', 1, function() {
- var values = falsey.concat(1, function() { return 1; });
+ var values = falsey.concat(1, _.constant(1));
var expected = _.transform(values, function(result, value) {
result.push(value, value);
@@ -6916,7 +7832,7 @@
];
test('should accept a falsey `array` argument', 1, function() {
- var expected = _.map(falsey, function() { return []; });
+ var expected = _.map(falsey, _.constant([]));
var actual = _.map(falsey, function(value, index) {
try {
@@ -6935,14 +7851,26 @@
deepEqual(_.rest(array, 2), [3]);
});
+ test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value == null ? [2, 3] : array;
+ });
+
+ var actual = _.map(falsey, function(n) {
+ return _.rest(array, n);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should return all elements when `n` < `1`', 3, function() {
- _.forEach([0, -1, -2], function(n) {
- deepEqual(_.rest(array, n), [1, 2, 3]);
+ _.each([0, -1, -Infinity], function(n) {
+ deepEqual(_.rest(array, n), array);
});
});
- test('should return an empty array when `n` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(n) {
+ test('should return an empty array when `n` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.rest(array, n), []);
});
});
@@ -6976,7 +7904,7 @@
deepEqual(args, [1, 0, array]);
});
- test('supports the `thisArg` argument', 1, function() {
+ test('should support the `thisArg` argument', 1, function() {
var actual = _.rest(array, function(num, index) {
return this[index] < 3;
}, array);
@@ -7043,14 +7971,26 @@
deepEqual(actual.sort(), array);
});
- test('should return an empty array when `n` < `1`', 3, function() {
- _.forEach([0, -1, -2], function(n) {
+ test('should treat falsey `n` values, except nullish, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value == null ? 1 : [];
+ });
+
+ var actual = _.map(falsey, function(n) {
+ return _.sample([1], n);
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should return an empty array when `n` < `1` or `NaN`', 3, function() {
+ _.each([0, -1, -Infinity], function(n) {
deepEqual(_.sample(array, n), []);
});
});
- test('should return all elements when `n` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(n) {
+ test('should return all elements when `n` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(n) {
deepEqual(_.sample(array, n).sort(), array);
});
});
@@ -7066,7 +8006,7 @@
result.push([], []);
});
- _.forEach(empties, function(value) {
+ _.each(empties, function(value) {
try {
actual.push(_.shuffle(value), _.shuffle(value, 1));
} catch(e) { }
@@ -7114,7 +8054,7 @@
}
});
- _.forEach({
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -7151,7 +8091,11 @@
deepEqual(actual.sort(), array);
});
- _.forEach({
+ test('should treat number values for `collection` as empty', 1, function() {
+ deepEqual(_.shuffle(1), []);
+ });
+
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -7172,15 +8116,15 @@
array = [1, 2, 3];
test('should return the number of own enumerable properties of an object', 1, function() {
- equal(_.size({ 'one': 1, 'two': 2, 'three': 3 }), 3);
+ strictEqual(_.size({ 'one': 1, 'two': 2, 'three': 3 }), 3);
});
test('should return the length of an array', 1, function() {
- equal(_.size(array), 3);
+ strictEqual(_.size(array), 3);
});
test('should accept a falsey `object` argument', 1, function() {
- var expected = _.map(falsey, function() { return 0; });
+ var expected = _.map(falsey, _.constant(0));
var actual = _.map(falsey, function(value, index) {
try {
@@ -7191,26 +8135,34 @@
deepEqual(actual, expected);
});
+ test('should work with `arguments` objects (test in IE < 9)', 1, function() {
+ strictEqual(_.size(args), 3);
+ });
+
test('should work with jQuery/MooTools DOM query collections', 1, function() {
function Foo(elements) { push.apply(this, elements); }
Foo.prototype = { 'length': 0, 'splice': Array.prototype.splice };
- equal(_.size(new Foo(array)), 3);
+ strictEqual(_.size(new Foo(array)), 3);
});
- test('should work with `arguments` objects (test in IE < 9)', 1, function() {
- if (!isPhantomPage) {
- equal(_.size(args), 3);
- } else {
- skipTest();
- }
+ test('should not treat objects with negative lengths as array-like', 1, function() {
+ strictEqual(_.size({ 'length': -1 }), 1);
});
- test('fixes the JScript [[DontEnum]] bug (test in IE < 9)', 1, function() {
- equal(_.size(shadowedObject), 7);
+ test('should not treat objects with lengths larger than `maxSafeInteger` as array-like', 1, function() {
+ strictEqual(_.size({ 'length': maxSafeInteger + 1 }), 1);
});
- _.forEach({
+ test('should not treat objects with non-number lengths as array-like', 1, function() {
+ strictEqual(_.size({ 'length': '0' }), 1);
+ });
+
+ test('fixes the JScript `[[DontEnum]]` bug (test in IE < 9)', 1, function() {
+ strictEqual(_.size(shadowedObject), 7);
+ });
+
+ _.each({
'literal': 'abc',
'object': Object('abc')
},
@@ -7232,18 +8184,28 @@
deepEqual(_.slice(array, 1), [2, 3]);
});
- test('should work with a `start` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(start) {
+ test('should work with a `start` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(start) {
deepEqual(_.slice(array, start), []);
});
});
+ test('should treat falsey `start` values as `0`', 1, function() {
+ var expected = _.map(falsey, _.constant(array));
+
+ var actual = _.map(falsey, function(start) {
+ return _.slice(array, start);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should work with a negative `start`', 1, function() {
deepEqual(_.slice(array, -1), [3]);
});
- test('should work with a negative `start` <= negative `array.length`', 2, function() {
- _.forEach([-3, -4], function(start) {
+ test('should work with a negative `start` <= negative `array.length`', 3, function() {
+ _.each([-3, -4, -Infinity], function(start) {
deepEqual(_.slice(array, start), [1, 2, 3]);
});
});
@@ -7252,21 +8214,38 @@
deepEqual(_.slice(array, 0, 1), [1]);
});
- test('should work with a `end` >= `array.length`', 2, function() {
- _.forEach([3, 4], function(end) {
+ test('should work with a `end` >= `array.length`', 4, function() {
+ _.each([3, 4, Math.pow(2, 32), Infinity], function(end) {
deepEqual(_.slice(array, 0, end), [1, 2, 3]);
});
});
+ test('should treat falsey `end` values, except `undefined`, as `0`', 1, function() {
+ var expected = _.map(falsey, function(value) {
+ return value === undefined ? array : [];
+ });
+
+ var actual = _.map(falsey, function(end) {
+ return _.slice(array, 0, end);
+ });
+
+ deepEqual(actual, expected);
+ });
+
test('should work with a negative `end`', 1, function() {
deepEqual(_.slice(array, 0, -1), [1, 2]);
});
- test('should work with a negative `end` <= negative `array.length`', 2, function() {
- _.forEach([-3, -4], function(end) {
+ test('should work with a negative `end` <= negative `array.length`', 3, function() {
+ _.each([-3, -4, -Infinity], function(end) {
deepEqual(_.slice(array, 0, end), []);
});
});
+
+ test('should coerce `start` and `end` to finite numbers', 1, function() {
+ var actual = [_.slice(array, '0', 1), _.slice(array, 0, '1'), _.slice(array, '1'), _.slice(array, NaN, 1), _.slice(array, 1, NaN)];
+ deepEqual(actual, [[1], [1], [2, 3], [1], []]);
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -7275,7 +8254,7 @@
(function() {
test('should return `false` for empty or falsey collections', 1, function() {
- var expected = _.map(empties, function() { return false; });
+ var expected = _.map(empties, _.constant(false));
var actual = _.map(empties, function(value) {
try {
@@ -7286,7 +8265,7 @@
deepEqual(actual, expected);
});
- test('should return `true` if the callback returns truey for any element in the collection', 2, function() {
+ test('should return `true` if the callback returns truthy for any element in the collection', 2, function() {
strictEqual(_.some([false, 1, ''], _.identity), true);
strictEqual(_.some([null, 'x', 0], _.identity), true);
});
@@ -7296,7 +8275,7 @@
strictEqual(_.some([null, 0, ''], _.identity), false);
});
- test('should return `true` as soon as the `callback` result is truey', 1, function() {
+ test('should return `true` as soon as the `callback` result is truthy', 1, function() {
strictEqual(_.some([null, true, null], _.identity), true);
});
@@ -7398,6 +8377,10 @@
deepEqual(actual, [3, 1, 2]);
});
+ test('should treat number values for `collection` as empty', 1, function() {
+ deepEqual(_.sortBy(1), []);
+ });
+
test('should support sorting by an array of properties', 1, function() {
var actual = _.sortBy(objects, ['a', 'b']);
deepEqual(actual, [objects[2], objects[0], objects[3], objects[1]]);
@@ -7428,8 +8411,8 @@
objects = [{ 'x': 20 }, { 'x': 30 }, { 'x': 50 }];
test('should return the insert index of a given value', 2, function() {
- equal(_.sortedIndex(array, 40), 2);
- equal(_.sortedIndex(array, 30), 1);
+ strictEqual(_.sortedIndex(array, 40), 2);
+ strictEqual(_.sortedIndex(array, 30), 1);
});
test('should pass the correct `callback` arguments', 1, function() {
@@ -7452,7 +8435,7 @@
test('should work with a string for `callback`', 1, function() {
var actual = _.sortedIndex(objects, { 'x': 40 }, 'x');
- equal(actual, 2);
+ strictEqual(actual, 2);
});
test('supports arrays with lengths larger than `Math.pow(2, 31) - 1`', 1, function() {
@@ -7464,7 +8447,7 @@
if (array.length == length) {
array[index] = index;
_.sortedIndex(array, index, function() { steps++; });
- equal(steps, 33);
+ strictEqual(steps, 33);
}
else {
skipTest();
@@ -7478,7 +8461,9 @@
(function() {
test('should contain properties with boolean values', 1, function() {
- ok(_.every(_.values(_.support), _.isBoolean));
+ ok(_.every(_.values(_.support), function(value) {
+ return value === true || value === false;
+ }));
});
test('should not contain minified properties (test production builds)', 1, function() {
@@ -7494,17 +8479,94 @@
'nodeClass',
'nonEnumArgs',
'nonEnumShadows',
+ 'nonEnumStrings',
'ownLast',
'spliceObjects',
'unindexedChars'
];
- ok(!_.size(_.difference(_.keys(_.support), props)));
+ ok(_.isEmpty(_.difference(_.keys(_.support), props)));
});
}());
/*--------------------------------------------------------------------------*/
+ QUnit.module('lodash.startsWith');
+
+ (function() {
+ var string = 'abc';
+
+ test('should return `true` if a string starts with `target`', 1, function() {
+ strictEqual(_.startsWith(string, 'a'), true);
+ });
+
+ test('should return `false` if a string does not start with `target`', 1, function() {
+ strictEqual(_.startsWith(string, 'b'), false);
+ });
+
+ test('should work with a `position` argument', 1, function() {
+ strictEqual(_.startsWith(string, 'b', 1), true);
+ });
+
+ test('should work with `position` >= `string.length`', 4, function() {
+ _.each([3, 5, maxSafeInteger, Infinity], function(position) {
+ strictEqual(_.startsWith(string, 'a', position), false);
+ });
+ });
+
+ test('should treat falsey `position` values as `0`', 1, function() {
+ var expected = _.map(falsey, _.constant(true));
+
+ var actual = _.map(falsey, function(position) {
+ return _.startsWith(string, 'a', position);
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should treat a negative `position` as `0`', 6, function() {
+ _.each([-1, -3, -Infinity], function(position) {
+ strictEqual(_.startsWith(string, 'a', position), true);
+ strictEqual(_.startsWith(string, 'b', position), false);
+ });
+ });
+
+ test('should always return `true` when `target` is an empty string regardless of `position`', 1, function() {
+ ok(_.every([-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, maxSafeInteger, Infinity], function(position) {
+ return _.startsWith(string, '', position, true);
+ }));
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('lodash.startsWith and lodash.endsWith');
+
+ _.each(['startsWith', 'endsWith'], function(methodName) {
+ var func = _[methodName],
+ isEndsWith = methodName == 'endsWith',
+ chr = isEndsWith ? 'c' : 'a',
+ string = 'abc';
+
+ test('`_.' + methodName + '` should coerce `string` to a string', 2, function() {
+ strictEqual(func(Object(string), chr), true);
+ strictEqual(func({ 'toString': _.constant(string) }, chr), true);
+ });
+
+ test('`_.' + methodName + '` should coerce `target` to a string', 2, function() {
+ strictEqual(func(string, Object(chr)), true);
+ strictEqual(func(string, { 'toString': _.constant(chr) }), true);
+ });
+
+ test('`_.' + methodName + '` should coerce `position` to a number', 2, function() {
+ var position = isEndsWith ? 2 : 1;
+ strictEqual(func(string, 'b', Object(position)), true);
+ strictEqual(func(string, 'b', { 'toString': _.constant(String(position)) }), true);
+ });
+ });
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash.tap');
(function() {
@@ -7569,7 +8631,7 @@
unescaped = '&<>"\'\/';
var compiled = _.template('<%- value %>
');
- equal(compiled({ 'value': unescaped }), escaped);
+ strictEqual(compiled({ 'value': unescaped }), escaped);
});
test('should evaluate JavaScript in "evaluate" delimiters', 1, function() {
@@ -7581,24 +8643,24 @@
);
var actual = compiled({ 'collection': { 'a': 'A', 'b': 'B' } });
- equal(actual, '');
+ strictEqual(actual, '');
});
test('should interpolate data object properties', 1, function() {
var compiled = _.template('<%= a %>BC');
- equal(compiled({ 'a': 'A' }), 'ABC');
+ strictEqual(compiled({ 'a': 'A' }), 'ABC');
});
test('should support escaped values in "interpolation" delimiters', 1, function() {
var compiled = _.template('<%= a ? "a=\\"A\\"" : "" %>');
- equal(compiled({ 'a': true }), 'a="A"');
+ strictEqual(compiled({ 'a': true }), 'a="A"');
});
test('should work with "interpolate" delimiters containing ternary operators', 1, function() {
var compiled = _.template('<%= value ? value : "b" %>'),
data = { 'value': 'a' };
- equal(compiled(data), 'a');
+ strictEqual(compiled(data), 'a');
});
test('should work with "interpolate" delimiters containing global values', 1, function() {
@@ -7608,11 +8670,11 @@
var actual = compiled();
} catch(e) { }
- equal(actual, 'function');
+ strictEqual(actual, 'function');
});
test('should work with complex "interpolate" delimiters', 22, function() {
- _.forEach({
+ _.each({
'<%= a + b %>': '3',
'<%= b - a %>': '1',
'<%= a = b %>': '2',
@@ -7640,19 +8702,19 @@
var compiled = _.template(key),
data = { 'a': 1, 'b': 2 };
- equal(compiled(data), value, key);
+ strictEqual(compiled(data), value, key);
});
});
test('should parse ES6 template delimiters', 2, function() {
var data = { 'value': 2 };
strictEqual(_.template('1${value}3', data), '123');
- equal(_.template('${"{" + value + "\\}"}', data), '{2}');
+ strictEqual(_.template('${"{" + value + "\\}"}', data), '{2}');
});
test('should not reference `_.escape` when "escape" delimiters are not used', 1, function() {
var compiled = _.template('<%= typeof __e %>');
- equal(compiled({}), 'undefined');
+ strictEqual(compiled({}), 'undefined');
});
test('should allow referencing variables declared in "evaluate" delimiters from other delimiters', 1, function() {
@@ -7664,7 +8726,7 @@
test('should support single line comments in "evaluate" delimiters (test production builds)', 1, function() {
var compiled = _.template('<% // comment %><% if (value) { %>yap<% } else { %>nope<% } %>');
- equal(compiled({ 'value': true }), 'yap');
+ strictEqual(compiled({ 'value': true }), 'yap');
});
test('should work with custom `_.templateSettings` delimiters', 1, function() {
@@ -7676,10 +8738,10 @@
'interpolate': /\{\{=([\s\S]+?)\}\}/g
});
- var compiled = _.template('{{ _.forEach(collection, function(value, index) { }}- {{= index }}: {{- value }}
{{ }); }}
'),
+ var compiled = _.template('{{ _.each(collection, function(value, index) { }}- {{= index }}: {{- value }}
{{ }); }}
'),
expected = '';
- equal(compiled({ 'collection': ['a & A', 'b & B'] }), expected);
+ strictEqual(compiled({ 'collection': ['a & A', 'b & B'] }), expected);
_.assign(_.templateSettings, settings);
});
@@ -7692,16 +8754,16 @@
'interpolate': /<\?=([\s\S]+?)\?>/g
});
- var compiled = _.template(' _.forEach(collection, function(value, index) { ?>- = index ?>: - value ?>
}); ?>
'),
+ var compiled = _.template(' _.each(collection, function(value, index) { ?>- = index ?>: - value ?>
}); ?>
'),
expected = '';
- equal(compiled({ 'collection': ['a & A', 'b & B'] }), expected);
+ strictEqual(compiled({ 'collection': ['a & A', 'b & B'] }), expected);
_.assign(_.templateSettings, settings);
});
test('should work with no delimiters', 1, function() {
var expected = 'abc';
- equal(_.template(expected, {}), expected);
+ strictEqual(_.template(expected, {}), expected);
});
test('should support the "imports" option', 1, function() {
@@ -7713,7 +8775,7 @@
test('should support the "variable" options', 1, function() {
var compiled = _.template(
- '<% _.forEach( data.a, function( value ) { %>' +
+ '<% _.each( data.a, function( value ) { %>' +
'<%= value.valueOf() %>' +
'<% }) %>', null, { 'variable': 'data' }
);
@@ -7727,32 +8789,32 @@
});
test('should use a `with` statement by default', 1, function() {
- var compiled = _.template('<%= index %><%= collection[index] %><% _.forEach(collection, function(value, index) { %><%= index %><% }); %>'),
+ var compiled = _.template('<%= index %><%= collection[index] %><% _.each(collection, function(value, index) { %><%= index %><% }); %>'),
actual = compiled({ 'index': 1, 'collection': ['a', 'b', 'c'] });
- equal(actual, '1b012');
+ strictEqual(actual, '1b012');
});
test('should work correctly with `this` references', 2, function() {
var compiled = _.template('a<%= this.String("b") %>c');
- equal(compiled(), 'abc');
+ strictEqual(compiled(), 'abc');
var object = { 'b': 'B' };
object.compiled = _.template('A<%= this.b %>C', null, { 'variable': 'obj' });
- equal(object.compiled(), 'ABC');
+ strictEqual(object.compiled(), 'ABC');
});
test('should work with backslashes', 1, function() {
var compiled = _.template('<%= a %> \\b');
- equal(compiled({ 'a': 'A' }), 'A \\b');
+ strictEqual(compiled({ 'a': 'A' }), 'A \\b');
});
test('should work with escaped characters in string literals', 2, function() {
var compiled = _.template('<% print("\'\\n\\r\\t\\u2028\\u2029\\\\") %>');
- equal(compiled(), "'\n\r\t\u2028\u2029\\");
+ strictEqual(compiled(), "'\n\r\t\u2028\u2029\\");
compiled = _.template('\'\n\r\t<%= a %>\u2028\u2029\\"');
- equal(compiled({ 'a': 'A' }), '\'\n\r\tA\u2028\u2029\\"');
+ strictEqual(compiled({ 'a': 'A' }), '\'\n\r\tA\u2028\u2029\\"');
});
test('should handle \\u2028 & \\u2029 characters', 1, function() {
@@ -7767,7 +8829,7 @@
} %>"
);
- equal(compiled({ 'a': 'A' }), "'a',\"A\"");
+ strictEqual(compiled({ 'a': 'A' }), "'a',\"A\"");
});
test('should work with templates containing newlines and comments', 1, function() {
@@ -7777,7 +8839,7 @@
%><%= value %>
'
);
- equal(compiled({ 'value': 3 }), '6
');
+ strictEqual(compiled({ 'value': 3 }), '6
');
});
test('should not error with IE conditional comments enabled (test with development build)', 1, function() {
@@ -7797,7 +8859,7 @@
var compiled = _.template(''),
data = { 'type': 1 };
- equal(compiled(data), '');
+ strictEqual(compiled(data), '');
});
test('should evaluate delimiters once', 1, function() {
@@ -7810,10 +8872,10 @@
test('should match delimiters before escaping text', 1, function() {
var compiled = _.template('<<\n a \n>>', null, { 'evaluate': /<<(.*?)>>/g });
- equal(compiled(), '<<\n a \n>>');
+ strictEqual(compiled(), '<<\n a \n>>');
});
- test('should resolve `null` and `undefined` values to empty strings', 4, function() {
+ test('should resolve `null` and `undefined` values to an empty string', 4, function() {
var compiled = _.template('<%= a %><%- a %>');
strictEqual(compiled({ 'a': null }), '');
strictEqual(compiled({ 'a': undefined }), '');
@@ -7828,14 +8890,14 @@
compiled = _.template(expected, null, { 'evaluate': /<<(.+?)>>/g }),
data = { 'value': true };
- equal(compiled(data), expected);
+ strictEqual(compiled(data), expected);
});
test('should support recursive calls', 1, function() {
var compiled = _.template('<%= a %><% a = _.template(c, obj) %><%= a %>'),
data = { 'a': 'A', 'b': 'B', 'c': '<%= b %>' };
- equal(compiled(data), 'AB');
+ strictEqual(compiled(data), 'AB');
});
test('should coerce `text` argument to a string', 1, function() {
@@ -7852,10 +8914,10 @@
});
test('should not modify `_.templateSettings` when `options` are provided', 2, function() {
- equal('a' in _.templateSettings, false);
+ ok(!('a' in _.templateSettings));
_.template('', {}, { 'a': 1 });
- equal('a' in _.templateSettings, false);
+ ok(!('a' in _.templateSettings));
delete _.templateSettings.a;
});
@@ -7892,6 +8954,60 @@
/*--------------------------------------------------------------------------*/
+ QUnit.module('lodash.truncate');
+
+ (function() {
+ var string = 'hi-diddly-ho there, neighborino';
+
+ test('should truncate to a length of `30` by default', 1, function() {
+ strictEqual(_.truncate(string), 'hi-diddly-ho there, neighbo...');
+ });
+
+ test('should not truncate if `string` is <= `length`', 2, function() {
+ strictEqual(_.truncate(string, string.length), string);
+ strictEqual(_.truncate(string, string.length + 2), string);
+ });
+
+ test('should truncate string the given length', 1, function() {
+ strictEqual(_.truncate(string, 24), 'hi-diddly-ho there, n...');
+ });
+
+ test('should support a `omission` option', 1, function() {
+ strictEqual(_.truncate(string, { 'omission': ' [...]' }), 'hi-diddly-ho there, neig [...]');
+ });
+
+ test('should support a `length` option', 1, function() {
+ strictEqual(_.truncate(string, { 'length': 4 }), 'h...');
+ });
+
+ test('should support a `separator` option', 2, function() {
+ strictEqual(_.truncate(string, { 'length': 24, 'separator': ' ' }), 'hi-diddly-ho there,...');
+ strictEqual(_.truncate(string, { 'length': 24, 'separator': /,? +/ }), 'hi-diddly-ho there...');
+ });
+
+ test('should treat negative `length` as `0`', 4, function() {
+ _.each([0, -2], function(length) {
+ strictEqual(_.truncate(string, length), '...');
+ strictEqual(_.truncate(string, { 'length': length }), '...');
+ });
+ });
+
+ test('should coerce `length` to a number', 4, function() {
+ _.each(['', '4'], function(length, index) {
+ var actual = index ? 'h...' : '...';
+ strictEqual(_.truncate(string, length), actual);
+ strictEqual(_.truncate(string, { 'length': { 'valueOf': _.constant(length) } }), actual);
+ });
+ });
+
+ test('should coerce `string` to a string', 2, function() {
+ strictEqual(_.truncate(Object(string), 4), 'h...');
+ strictEqual(_.truncate({ 'toString': _.constant(string) }, 5), 'hi...');
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash.throttle');
(function() {
@@ -7920,7 +9036,7 @@
asyncTest('subsequent calls should return the result of the first call', 5, function() {
if (!(isRhino && isModularize)) {
- var throttled = _.throttle(function(value) { return value; }, 32),
+ var throttled = _.throttle(_.identity, 32),
result = [throttled('a'), throttled('b')];
deepEqual(result, ['a', 'a']);
@@ -7965,7 +9081,7 @@
throttled();
setTimeout(function() {
- equal(callCount, 2);
+ strictEqual(callCount, 2);
QUnit.start();
}, 64);
}
@@ -7991,7 +9107,7 @@
}, 32);
throttled.call(object, 'a');
- equal(count, 1);
+ strictEqual(count, 1);
setTimeout(function() {
ok(count < 3);
@@ -8011,10 +9127,10 @@
throttled = _.throttle(function() { count++; }, 32);
throttled();
- equal(count, 1);
+ strictEqual(count, 1);
setTimeout(function() {
- equal(count, 1);
+ strictEqual(count, 1);
QUnit.start();
}, 64);
}
@@ -8056,8 +9172,8 @@
return value;
}, 32, {});
- equal(throttled('a'), 'a');
- equal(throttled('b'), 'a');
+ strictEqual(throttled('a'), 'a');
+ strictEqual(throttled('b'), 'a');
setTimeout(function() {
strictEqual(count, 2);
@@ -8070,14 +9186,14 @@
}
});
- test('should work with `leading` option', 4, function() {
+ test('should support a `leading` option', 4, function() {
if (!(isRhino && isModularize)) {
- _.forEach([true, { 'leading': true }], function(options) {
+ _.each([true, { 'leading': true }], function(options) {
var withLeading = _.throttle(_.identity, 32, options);
- equal(withLeading('a'), 'a');
+ strictEqual(withLeading('a'), 'a');
});
- _.forEach([false, { 'leading': false }], function(options) {
+ _.each([false, { 'leading': false }], function(options) {
var withoutLeading = _.throttle(_.identity, 32, options);
strictEqual(withoutLeading('a'), undefined);
});
@@ -8087,7 +9203,7 @@
}
});
- asyncTest('should work with `trailing` option', 6, function() {
+ asyncTest('should support a `trailing` option', 6, function() {
if (!(isRhino && isModularize)) {
var withCount = 0,
withoutCount = 0;
@@ -8102,14 +9218,14 @@
return value;
}, 64, { 'trailing': false });
- equal(withTrailing('a'), 'a');
- equal(withTrailing('b'), 'a');
+ strictEqual(withTrailing('a'), 'a');
+ strictEqual(withTrailing('b'), 'a');
- equal(withoutTrailing('a'), 'a');
- equal(withoutTrailing('b'), 'a');
+ strictEqual(withoutTrailing('a'), 'a');
+ strictEqual(withoutTrailing('b'), 'a');
setTimeout(function() {
- equal(withCount, 2);
+ strictEqual(withCount, 2);
strictEqual(withoutCount, 1);
QUnit.start();
}, 256);
@@ -8152,14 +9268,15 @@
QUnit.module('lodash.debounce and lodash.throttle');
- _.forEach(['debounce', 'throttle'], function(methodName) {
- var func = _[methodName];
+ _.each(['debounce', 'throttle'], function(methodName) {
+ var func = _[methodName],
+ isThrottle = methodName == 'throttle';
test('_.' + methodName + ' should not error for non-object `options` values', 1, function() {
var pass = true;
try {
- func(noop, 32, 1);
+ func(_.noop, 32, 1);
} catch(e) {
pass = false;
}
@@ -8175,11 +9292,11 @@
};
object.funced();
- if (methodName == 'throttle') {
+ if (isThrottle) {
object.funced();
}
setTimeout(function() {
- deepEqual(actual, methodName == 'throttle' ? [object, object] : [object]);
+ deepEqual(actual, isThrottle ? [object, object] : [object]);
QUnit.start();
}, 64);
}
@@ -8212,7 +9329,7 @@
setTimeout(function() {
funced();
- equal(callCount, methodName == 'throttle' ? 2 : 1);
+ strictEqual(callCount, isThrottle ? 2 : 1);
QUnit.start();
}, 64);
}
@@ -8245,7 +9362,7 @@
QUnit.module('lodash.slice and lodash.toArray');
- _.forEach(['slice', 'toArray'], function(methodName) {
+ _.each(['slice', 'toArray'], function(methodName) {
var args = (function() { return arguments; }(1, 2, 3)),
array = [1, 2, 3],
func = _[methodName];
@@ -8256,8 +9373,8 @@
var actual = func(sparse);
- ok(0 in actual);
- ok(2 in actual);
+ ok('0' in actual);
+ ok('2' in actual);
deepEqual(actual, sparse);
});
@@ -8291,6 +9408,17 @@
QUnit.module('lodash.times');
(function() {
+ test('should rollover large `n` values', 1, function() {
+ var actual = _.times(Math.pow(2, 32) + 1);
+ deepEqual(actual, [0]);
+ });
+
+ test('should coerce non-finite `n` values to `0`', 3, function() {
+ _.each([-Infinity, NaN, Infinity], function(n) {
+ deepEqual(_.times(n), []);
+ });
+ });
+
test('should pass the correct `callback` arguments', 1, function() {
var args;
@@ -8322,7 +9450,7 @@
test('should return an empty array for falsey and negative `n` arguments', 1, function() {
var values = falsey.concat(-1, -Infinity),
- expected = _.map(values, function() { return []; });
+ expected = _.map(values, _.constant([]));
var actual = _.map(values, function(value, index) {
return index ? _.times(value) : _.times();
@@ -8376,7 +9504,11 @@
ok(_.transform(new Foo) instanceof Foo);
});
- _.forEach({
+ test('should check that `object` is an object before using it as the `accumulator` `[[Prototype]]', 1, function() {
+ ok(!(_.transform(1) instanceof Number));
+ });
+
+ _.each({
'array': [1, 2, 3],
'object': { 'a': 1, 'b': 2, 'c': 3 }
},
@@ -8390,10 +9522,10 @@
var first = args[0];
if (key == 'array') {
- ok(first != object && _.isArray(first));
+ ok(first !== object && _.isArray(first));
deepEqual(args, [first, 1, 0, object]);
} else {
- ok(first != object && _.isPlainObject(first));
+ ok(first !== object && _.isPlainObject(first));
deepEqual(args, [first, 1, 'a', object]);
}
});
@@ -8413,7 +9545,7 @@
QUnit.module('trim methods');
- _.forEach(['trim', 'trimLeft', 'trimRight'], function(methodName, index) {
+ _.each(['trim', 'trimLeft', 'trimRight'], function(methodName, index) {
var func = _[methodName];
var parts = [];
@@ -8430,6 +9562,13 @@
strictEqual(func(string), (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''));
});
+ test('`_.' + methodName + '` should not remove non-whitespace characters', 1, function() {
+ var problemChars = '\x85\u200b\ufffe',
+ string = problemChars + 'a b c' + problemChars;
+
+ strictEqual(func(string), string);
+ });
+
test('`_.' + methodName + '` should coerce `string` to a string', 1, function() {
var object = { 'toString': function() { return whitespace + 'a b c' + whitespace; } };
strictEqual(func(object), (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''));
@@ -8447,13 +9586,22 @@
strictEqual(func(string, object), (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : ''));
});
- test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty strings', 6, function() {
- _.forEach([null, '_-'], function(arg) {
- strictEqual(func.call(_, null, arg), '');
- strictEqual(func.call(_, undefined, arg), '');
- strictEqual(func.call(_, '', arg), '');
+ test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string and `chars`', 6, function() {
+ _.each([null, '_-'], function(chars) {
+ strictEqual(func(null, chars), '');
+ strictEqual(func(undefined, chars), '');
+ strictEqual(func('', chars), '');
});
});
+
+ test('`_.' + methodName + '` should work with `null`, `undefined`, or empty string for `chars`', 3, function() {
+ var string = whitespace + 'a b c' + whitespace,
+ expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : '');
+
+ strictEqual(func(string, null), expected);
+ strictEqual(func(string, undefined), expected);
+ strictEqual(func(string, ''), string);
+ });
});
/*--------------------------------------------------------------------------*/
@@ -8465,29 +9613,23 @@
unescaped = '&<>"\'\/';
test('should unescape entities in the correct order', 1, function() {
- equal(_.unescape('<'), '<');
+ strictEqual(_.unescape('<'), '<');
});
test('should unescape the proper entities', 1, function() {
- equal(_.unescape(escaped), unescaped);
+ strictEqual(_.unescape(escaped), unescaped);
});
test('should not unescape the "/" entity', 1, function() {
- equal(_.unescape('/'), '/');
+ strictEqual(_.unescape('/'), '/');
});
test('should handle strings with nothing to unescape', 1, function() {
- equal(_.unescape('abc'), 'abc');
+ strictEqual(_.unescape('abc'), 'abc');
});
test('should unescape the same characters escaped by `_.escape`', 1, function() {
- equal(_.unescape(_.escape(unescaped)), unescaped);
- });
-
- test('should return an empty string when provided `null`, `undefined`, or empty strings', 3, function() {
- strictEqual(_.unescape(null), '');
- strictEqual(_.unescape(undefined), '');
- strictEqual(_.unescape(''), '');
+ strictEqual(_.unescape(_.escape(unescaped)), unescaped);
});
}());
@@ -8496,28 +9638,25 @@
QUnit.module('lodash.union');
(function() {
+ var args = arguments;
+
test('should return the union of the given arrays', 1, function() {
- var actual = _.union([1, 2, 3], [5, 2, 1, 4], [2, 1]);
- deepEqual(actual, [1, 2, 3, 5, 4]);
+ var actual = _.union([1, 3, 2], [5, 2, 1, 4], [2, 1]);
+ deepEqual(actual, [1, 3, 2, 5, 4]);
});
test('should not flatten nested arrays', 1, function() {
- var actual = _.union([1, 2, 3], [1, [5]], [2, [4]]);
- deepEqual(actual, [1, 2, 3, [5], [4]]);
+ var actual = _.union([1, 3, 2], [1, [5]], [2, [4]]);
+ deepEqual(actual, [1, 3, 2, [5], [4]]);
});
- test('should produce correct results when provided a falsey `array` argument', 1, function() {
- var expected = [1, 2, 3],
- actual = _.union(null, expected);
-
- deepEqual(actual, expected);
+ test('should ignore values that are not arrays or `arguments` objects', 3, function() {
+ var array = [0];
+ deepEqual(_.union(array, 3, null, { '0': 1 }), array);
+ deepEqual(_.union(null, array, null, [2, 1]), [0, 2, 1]);
+ deepEqual(_.union(null, array, null, args), [0, 1, 2, 3]);
});
-
- test('should ignore individual secondary values', 1, function() {
- var array = [1];
- deepEqual(_.union(array, 1, 2, 3), array);
- });
- }());
+ }(1, 2, 3));
/*--------------------------------------------------------------------------*/
@@ -8575,7 +9714,7 @@
test('should work with large arrays', 1, function() {
var object = {};
- var largeArray = _.times(LARGE_ARRAY_SIZE, function(index) {
+ var largeArray = _.times(largeArraySize, function(index) {
switch (index % 3) {
case 0: return 0;
case 1: return 'a';
@@ -8589,18 +9728,19 @@
test('should work with large arrays of boolean, `null`, and `undefined` values', 1, function() {
var array = [],
expected = [true, false, null, undefined],
- count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
+ count = Math.ceil(largeArraySize / expected.length);
_.times(count, function() {
push.apply(array, expected);
});
+
deepEqual(_.uniq(array), expected);
});
test('should distinguish between numbers and numeric strings', 1, function() {
var array = [],
expected = ['2', 2, Object('2'), Object(2)],
- count = Math.ceil(LARGE_ARRAY_SIZE / expected.length);
+ count = Math.ceil(largeArraySize / expected.length);
_.times(count, function() {
push.apply(array, expected);
@@ -8609,7 +9749,7 @@
deepEqual(_.uniq(array), expected);
});
- _.forEach({
+ _.each({
'an object': ['a'],
'a number': 0,
'a string': '0'
@@ -8637,11 +9777,11 @@
actual.push(_.uniqueId());
});
- equal(_.uniq(actual).length, actual.length);
+ strictEqual(_.uniq(actual).length, actual.length);
});
test('should return a string value when not passing a prefix argument', 1, function() {
- equal(typeof _.uniqueId(), 'string');
+ strictEqual(typeof _.uniqueId(), 'string');
});
test('should coerce the prefix argument to a string', 1, function() {
@@ -8671,7 +9811,7 @@
QUnit.module('lodash.where');
(function() {
- var array = [
+ var objects = [
{ 'a': 1 },
{ 'a': 1 },
{ 'a': 1, 'b': 2 },
@@ -8679,22 +9819,27 @@
{ 'a': 3 }
];
- test('should filter by properties', 6, function() {
- deepEqual(_.where(array, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]);
- deepEqual(_.where(array, { 'a': 2 }), [{ 'a': 2, 'b': 2 }]);
- deepEqual(_.where(array, { 'a': 3 }), [{ 'a': 3 }]);
- deepEqual(_.where(array, { 'b': 1 }), []);
- deepEqual(_.where(array, { 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]);
- deepEqual(_.where(array, { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }]);
+ test('should filter by `source` properties', 6, function() {
+ deepEqual(_.where(objects, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1 }, { 'a': 1, 'b': 2 }]);
+ deepEqual(_.where(objects, { 'a': 2 }), [{ 'a': 2, 'b': 2 }]);
+ deepEqual(_.where(objects, { 'a': 3 }), [{ 'a': 3 }]);
+ deepEqual(_.where(objects, { 'b': 1 }), []);
+ deepEqual(_.where(objects, { 'b': 2 }), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]);
+ deepEqual(_.where(objects, { 'a': 1, 'b': 2 }), [{ 'a': 1, 'b': 2 }]);
});
- test('should not filter by inherited properties', 1, function() {
+ test('should not filter by inherited `source` properties', 2, function() {
function Foo() {}
Foo.prototype = { 'a': 2 };
- var properties = new Foo;
- properties.b = 2;
- deepEqual(_.where(array, properties), [{ 'a': 1, 'b': 2 }, { 'a': 2, 'b': 2 }]);
+ var source = new Foo;
+ source.b = 2;
+
+ var expected = [objects[2], objects[3]],
+ actual = _.where(objects, source);
+
+ deepEqual(actual, expected);
+ ok(_.isEmpty(_.difference(actual, objects)));
});
test('should filter by problem JScript properties (test in IE < 9)', 1, function() {
@@ -8702,47 +9847,83 @@
deepEqual(_.where(collection, shadowedObject), [shadowedObject]);
});
- test('should work with an object for `collection`', 1, function() {
+ test('should work with an object for `collection`', 2, function() {
var collection = {
'x': { 'a': 1 },
'y': { 'a': 3 },
'z': { 'a': 1, 'b': 2 }
};
- deepEqual(_.where(collection, { 'a': 1 }), [{ 'a': 1 }, { 'a': 1, 'b': 2 }]);
+ var expected = [collection.x, collection.z],
+ actual = _.where(collection, { 'a': 1 });
+
+ deepEqual(actual, expected);
+ ok(_.isEmpty(_.difference(actual, _.values(collection))));
});
- test('should return an empty array when provided an empty `properties` object', 1, function() {
- deepEqual(_.where(array, {}), []);
+ test('should work with a function for `source`', 1, function() {
+ function source() {}
+ source.a = 2;
+
+ deepEqual(_.where(objects, source), [{ 'a': 2, 'b': 2 }]);
});
- test('should deep compare `properties` values', 1, function() {
+ test('should match all elements when provided an empty `source`', 1, function() {
+ var expected = _.map(empties, _.constant(objects));
+
+ var actual = _.map(empties, function(value) {
+ var result = _.where(objects, value);
+ return result !== objects && result;
+ });
+
+ deepEqual(actual, expected);
+ });
+
+ test('should perform a deep partial comparison of `source`', 2, function() {
var collection = [{ 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 }],
- expected = _.cloneDeep(collection);
+ expected = collection.slice(),
+ actual = _.where(collection, { 'a': { 'b': { 'c': 1 } } });
- deepEqual(_.where(collection, { 'a': { 'b': { 'c': 1 } } }), expected);
+ deepEqual(actual, expected);
+ ok(_.isEmpty(_.difference(actual, collection)));
});
test('should search of arrays for values', 2, function() {
var collection = [{ 'a': [1, 2] }],
- expected = _.cloneDeep(collection);
+ expected = collection.slice();
deepEqual(_.where(collection, { 'a': [] }), []);
deepEqual(_.where(collection, { 'a': [2] }), expected);
});
- test('should handle `properties` with `undefined` values', 4, function() {
- var properties = { 'b': undefined };
- deepEqual(_.where([{ 'a': 1 }, { 'a': 1, 'b': 1 }], properties), []);
+ test('should perform a partial comparison of *all* objects within arrays of `source`', 2, function() {
+ var collection = [
+ { 'a': [{ 'b': 1, 'c': 2, 'd': 3 }, { 'b': 4, 'c': 5, 'd': 6 }] },
+ { 'a': [{ 'b': 1, 'c': 2, 'd': 3 }, { 'b': 4, 'c': 6, 'd': 7 }] }
+ ];
+
+ var actual = _.where(collection, { 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 5 }] });
+ deepEqual(actual, [collection[0]]);
+ ok(_.isEmpty(_.difference(actual, collection)));
+ });
+
+ test('should handle a `source` with `undefined` values', 4, function() {
+ var source = { 'b': undefined },
+ actual = _.where([{ 'a': 1 }, { 'a': 1, 'b': 1 }], source);
+
+ deepEqual(actual, []);
var object = { 'a': 1, 'b': undefined };
- deepEqual(_.where([object], properties), [object]);
+ actual = _.where([object], source);
+ deepEqual(actual, [object]);
- properties = { 'a': { 'c': undefined } };
- deepEqual(_.where([{ 'a': { 'b': 1 } }, { 'a':{ 'b':1 , 'c': 1 } }], properties), []);
+ source = { 'a': { 'c': undefined } };
+ actual = _.where([{ 'a': { 'b': 1 } }, { 'a':{ 'b':1 , 'c': 1 } }], source);
+ deepEqual(actual, []);
object = { 'a': { 'b': 1, 'c': undefined } };
- deepEqual(_.where([object], properties), [object]);
+ actual = _.where([object], source);
+ deepEqual(actual, [object]);
});
}());
@@ -8776,18 +9957,18 @@
return '' + func(text) + '
';
});
- equal(p('fred, barney, & pebbles'), 'fred, barney, & pebbles
');
+ strictEqual(p('fred, barney, & pebbles'), 'fred, barney, & pebbles
');
});
test('should pass the correct `wrapper` arguments', 1, function() {
var args;
- var wrapped = _.wrap(noop, function() {
+ var wrapped = _.wrap(_.noop, function() {
args || (args = slice.call(arguments));
});
wrapped(1, 2, 3);
- deepEqual(args, [noop, 1, 2, 3]);
+ deepEqual(args, [_.noop, 1, 2, 3]);
});
test('should not set a `this` binding', 1, function() {
@@ -8796,7 +9977,7 @@
});
var object = { 'p': p, 'text': 'fred, barney, & pebbles' };
- equal(object.p(), 'fred, barney, & pebbles
');
+ strictEqual(object.p(), 'fred, barney, & pebbles
');
});
}());
@@ -8805,6 +9986,8 @@
QUnit.module('lodash.xor');
(function() {
+ var args = arguments;
+
test('should return the symmetric difference of the given arrays', 1, function() {
var actual = _.xor([1, 2, 5], [2, 3, 5], [3, 4, 5]);
deepEqual(actual, [1, 4, 5]);
@@ -8834,11 +10017,18 @@
}
});
- test('should ignore individual secondary values', 1, function() {
- var array = [1, null, 3];
- deepEqual(_.xor(array, 3, null), array);
+ test('should ignore individual secondary arguments', 1, function() {
+ var array = [0];
+ deepEqual(_.xor(array, 3, null, { '0': 1 }), array);
});
- }());
+
+ test('should ignore values that are not arrays or `arguments` objects', 3, function() {
+ var array = [1, 2];
+ deepEqual(_.xor(array, 3, null, { '0': 1 }), array);
+ deepEqual(_.xor(null, array, null, [2, 3]), [1, 3]);
+ deepEqual(_.xor(null, array, null, args), [3]);
+ });
+ }(1, 2, 3));
/*--------------------------------------------------------------------------*/
@@ -8879,11 +10069,11 @@
];
var actual = _.zip(pair[0]);
- ok(0 in actual[2]);
+ ok('0' in actual[2]);
deepEqual(actual, pair[1]);
actual = _.zip.apply(_, actual);
- ok(2 in actual[0]);
+ ok('2' in actual[0]);
deepEqual(actual, [['barney', 36, undefined], ['fred', 40, false]]);
});
@@ -8930,7 +10120,7 @@
});
test('should accept a falsey `array` argument', 1, function() {
- var expected = _.map(falsey, function() { return {}; });
+ var expected = _.map(falsey, _.constant({}));
var actual = _.map(falsey, function(value, index) {
try {
@@ -8961,7 +10151,7 @@
wrapped.shift();
deepEqual(wrapped.keys().value(), ['length']);
- equal(wrapped.first(), undefined);
+ strictEqual(wrapped.first(), undefined);
}
else {
skipTest(2);
@@ -8980,7 +10170,7 @@
wrapped.splice(0, 1);
deepEqual(wrapped.keys().value(), ['length']);
- equal(wrapped.first(), undefined);
+ strictEqual(wrapped.first(), undefined);
}
else {
skipTest(2);
@@ -8996,7 +10186,7 @@
test('should return the `toString` result of the wrapped value', 1, function() {
if (!isNpm) {
var wrapped = _([1, 2, 3]);
- equal(String(wrapped), '1,2,3');
+ strictEqual(String(wrapped), '1,2,3');
}
else {
skipTest();
@@ -9012,12 +10202,33 @@
test('should return the `valueOf` result of the wrapped value', 1, function() {
if (!isNpm) {
var wrapped = _(123);
- equal(Number(wrapped), 123);
+ strictEqual(Number(wrapped), 123);
}
else {
skipTest();
}
});
+
+ test('should stringify the wrapped value when passed to `JSON.stringify`', 1, function() {
+ if (!isNpm && JSON) {
+ var wrapped = _([1, 2, 3]);
+ strictEqual(JSON.stringify(wrapped), '[1,2,3]');
+ }
+ else {
+ skipTest();
+ }
+ });
+
+ test('should be aliased', 2, function() {
+ if (!isNpm) {
+ var expected = _.prototype.valueOf;
+ strictEqual(_.prototype.toJSON, expected);
+ strictEqual(_.prototype.value, expected);
+ }
+ else {
+ skipTest(2);
+ }
+ });
}());
/*--------------------------------------------------------------------------*/
@@ -9035,7 +10246,7 @@
'unshift'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return the existing wrapped value', 1, function() {
if (!isNpm) {
strictEqual(wrapped[methodName](), wrapped);
@@ -9061,7 +10272,7 @@
'splice'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return a new wrapped value', 1, function() {
if (!isNpm) {
ok(wrapped[methodName]() instanceof _);
@@ -9114,14 +10325,14 @@
'some'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` should return an unwrapped value', 1, function() {
if (!isNpm) {
var actual = methodName == 'reduceRight'
? wrapped[methodName](_.identity)
: wrapped[methodName]();
- equal(actual instanceof _, false);
+ ok(!(actual instanceof _));
}
else {
skipTest();
@@ -9144,10 +10355,10 @@
'sample'
];
- _.forEach(funcs, function(methodName) {
+ _.each(funcs, function(methodName) {
test('`_(...).' + methodName + '` called without an `n` argument should return an unwrapped value', 1, function() {
if (!isNpm) {
- equal(typeof wrapped[methodName](), 'number');
+ strictEqual(typeof wrapped[methodName](), 'number');
}
else {
skipTest();
@@ -9166,10 +10377,10 @@
test('`_.' + methodName + '` should return `undefined` when querying falsey arguments without an `n` argument', 1, function() {
if (!isNpm) {
var actual = [],
- expected = _.map(falsey, function() { return undefined; }),
+ expected = _.map(falsey, _.constant()),
func = _[methodName];
- _.forEach(falsey, function(value, index) {
+ _.each(falsey, function(value, index) {
try {
actual.push(index ? func(value) : func());
} catch(e) { }
@@ -9184,7 +10395,7 @@
test('`_.' + methodName + '` should return an empty array when querying falsey arguments with an `n` argument', 1, function() {
if (!isNpm) {
- var expected = _.map(falsey, function() { return []; }),
+ var expected = _.map(falsey, _.constant([])),
func = _[methodName];
var actual = _.map(falsey, function(value, index) {
@@ -9245,21 +10456,22 @@
deepEqual([args[0], args[1], args[2]], [1, [3], 5], message('pull'));
_.remove(args, function(value) { return typeof value == 'number'; });
- ok(args.length == 1 && _.isEqual(args[0], [3]), message('remove'));
+ ok(args.length === 1 && _.isEqual(args[0], [3]), message('remove'));
}
else {
skipTest(2)
}
});
- test('should accept falsey primary arguments', 3, function() {
+ test('should accept falsey primary arguments', 4, function() {
function message(methodName) {
return '`_.' + methodName + '` should accept falsey primary arguments';
}
- deepEqual(_.difference(null, array), [], message('difference'));
- deepEqual(_.intersection(null, array), [], message('intersection'));
+ deepEqual(_.difference(null, array), array, message('difference'));
+ deepEqual(_.intersection(null, array), array, message('intersection'));
deepEqual(_.union(null, array), array, message('union'));
+ deepEqual(_.xor(null, array), array, message('xor'));
});
test('should accept falsey secondary arguments', 3, function() {
@@ -9268,13 +10480,49 @@
}
deepEqual(_.difference(array, null), array, message('difference'));
- deepEqual(_.intersection(array, null), [], message('intersection'));
+ deepEqual(_.intersection(array, null), array, message('intersection'));
deepEqual(_.union(array, null), array, message('union'));
});
}(1, null, [3], null, 5));
/*--------------------------------------------------------------------------*/
+ /*--------------------------------------------------------------------------*/
+
+ QUnit.module('"Strings" category methods');
+
+ (function() {
+ var stringMethods = [
+ 'camelCase',
+ 'capitalize',
+ 'escape',
+ 'escapeRegExp',
+ 'kebabCase',
+ 'pad',
+ 'padLeft',
+ 'padRight',
+ 'repeat',
+ 'snakeCase',
+ 'trim',
+ 'trimLeft',
+ 'trimRight',
+ 'truncate',
+ 'unescape'
+ ];
+
+ _.each(stringMethods, function(methodName) {
+ var func = _[methodName];
+
+ test('`_.' + methodName + '` should return an empty string when provided `null`, `undefined`, or empty string', 3, function() {
+ strictEqual(func(null), '');
+ strictEqual(func(undefined), '');
+ strictEqual(func(''), '');
+ });
+ });
+ }());
+
+ /*--------------------------------------------------------------------------*/
+
QUnit.module('lodash methods');
(function() {
@@ -9299,10 +10547,10 @@
'pairs',
'pluck',
'pull',
+ 'pullAt',
'range',
'reject',
'remove',
- 'removeAt',
'rest',
'sample',
'shuffle',
@@ -9327,6 +10575,7 @@
'defer',
'delay',
'memoize',
+ 'negate',
'once',
'partial',
'partialRight',
@@ -9337,12 +10586,12 @@
var acceptFalsey = _.difference(allMethods, rejectFalsey);
- test('should accept falsey arguments', 167, function() {
- var emptyArrays = _.map(falsey, function() { return []; }),
+ test('should accept falsey arguments', 187, function() {
+ var emptyArrays = _.map(falsey, _.constant([])),
isExposed = '_' in root,
oldDash = root._;
- _.forEach(acceptFalsey, function(methodName) {
+ _.each(acceptFalsey, function(methodName) {
var expected = emptyArrays,
func = _[methodName],
pass = true;
@@ -9373,7 +10622,7 @@
});
// skip tests for missing methods of modularized builds
- _.forEach(['noConflict', 'runInContext', 'tap'], function(methodName) {
+ _.each(['noConflict', 'runInContext', 'tap'], function(methodName) {
if (!_[methodName]) {
skipTest();
}
@@ -9383,7 +10632,7 @@
test('should return an array', 66, function() {
var array = [1, 2, 3];
- _.forEach(returnArrays, function(methodName) {
+ _.each(returnArrays, function(methodName) {
var actual,
func = _[methodName];
@@ -9402,13 +10651,13 @@
ok(_.isArray(actual), '_.' + methodName + ' returns an array');
var isPull = methodName == 'pull';
- equal(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the provided array');
+ strictEqual(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the provided array');
});
});
- test('should reject falsey arguments', 14, function() {
- _.forEach(rejectFalsey, function(methodName) {
- var expected = _.map(falsey, function() { return true; }),
+ test('should throw a TypeError for falsey arguments', 15, function() {
+ _.each(rejectFalsey, function(methodName) {
+ var expected = _.map(falsey, _.constant(true)),
func = _[methodName];
var actual = _.map(falsey, function(value, index) {
@@ -9425,14 +10674,18 @@
});
});
- test('should handle `null` `thisArg` arguments', 30, function() {
- var thisArg,
- callback = function() { thisArg = this; },
- expected = (function() { return this; }).call(null);
+ test('should handle `null` `thisArg` arguments', 44, function() {
+ var expected = (function() { return this; }).call(null);
var funcs = [
+ 'assign',
+ 'clone',
+ 'cloneDeep',
'countBy',
+ 'dropWhile',
+ 'dropRightWhile',
'every',
+ 'flatten',
'filter',
'find',
'findIndex',
@@ -9447,10 +10700,14 @@
'forOwn',
'forOwnRight',
'groupBy',
+ 'isEqual',
'map',
+ 'mapValues',
'max',
+ 'merge',
'min',
'omit',
+ 'partition',
'pick',
'reduce',
'reduceRight',
@@ -9459,38 +10716,46 @@
'some',
'sortBy',
'sortedIndex',
+ 'takeWhile',
+ 'takeRightWhile',
+ 'tap',
'times',
+ 'transform',
'uniq'
];
- _.forEach(funcs, function(methodName) {
- var array = ['a'],
+ _.each(funcs, function(methodName) {
+ var actual,
+ array = ['a'],
func = _[methodName],
message = '`_.' + methodName + '` handles `null` `thisArg` arguments';
- thisArg = undefined;
-
- if (/^reduce/.test(methodName)) {
- func(array, callback, 0, null);
- } else if (methodName == 'sortedIndex') {
- func(array, 'a', callback, null);
- } else if (methodName == 'times') {
- func(1, callback, null);
- } else {
- func(array, callback, null);
+ function callback() {
+ actual = this;
}
-
- if (expected === null) {
- strictEqual(thisArg, null, message);
- } else {
- equal(thisArg, expected, message);
+ if (func) {
+ if (/^reduce/.test(methodName) || methodName == 'transform') {
+ func(array, callback, 0, null);
+ } else if (_.contains(['assign', 'merge'], methodName)) {
+ func(array, array, callback, null);
+ } else if (_.contains(['isEqual', 'sortedIndex'], methodName)) {
+ func(array, 'a', callback, null);
+ } else if (methodName == 'times') {
+ func(1, callback, null);
+ } else {
+ func(array, callback, null);
+ }
+ strictEqual(actual, expected, message);
+ }
+ else {
+ skipTest();
}
});
});
test('should not contain minified method names (test production builds)', 1, function() {
ok(_.every(_.functions(_), function(methodName) {
- return methodName.length > 2 || methodName == 'at';
+ return methodName.length > 2 || methodName === 'at';
}));
});
}());