diff --git a/.jamignore b/.jamignore new file mode 100644 index 0000000000..faf22fdc0a --- /dev/null +++ b/.jamignore @@ -0,0 +1,7 @@ +*.custom.* +.* +dist/ +doc/*.php +node_modules/ +perf/*.sh +test/*.sh diff --git a/.npmignore b/.npmignore index 02c82644f8..a21c8bba85 100644 --- a/.npmignore +++ b/.npmignore @@ -1,5 +1,4 @@ *.custom.* -*.min.* .* dist/ doc/*.php @@ -11,7 +10,7 @@ test/*-ui.js test/*.sh vendor/backbone/ vendor/docdown/ +vendor/firebug-lite/ vendor/qunit/qunit/*.css -vendor/underscore/ vendor/requirejs/ -vendor/firebug-lite/ +vendor/underscore/test/ diff --git a/README.md b/README.md index e0d8492004..d7ccec0e86 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,14 @@ -# Lo-Dash v0.4.2 +# Lo-Dash v0.5.0 -A drop-in replacement[*](https://github.com/bestiejs/lodash/wiki/Drop-in-Disclaimer) for Underscore.js, from the devs behind [jsPerf.com](http://jsperf.com), that delivers [performance improvements](http://lodash.com/benchmarks), [bug fixes](https://github.com/bestiejs/lodash#closed-underscorejs-issues), and [additional features](https://github.com/bestiejs/lodash#features). +A drop-in replacement[*](https://github.com/bestiejs/lodash/wiki/Drop-in-Disclaimer) for Underscore.js, from the devs behind [jsPerf.com](http://jsperf.com), delivering [performance](http://lodash.com/benchmarks), [bug fixes](https://github.com/bestiejs/lodash#resolved-underscorejs-issues-20), and [additional features](https://github.com/bestiejs/lodash#features). Lo-Dash’s performance is gained by avoiding slower native methods, instead opting for simplified non-ES5 compliant methods optimized for common usage, and by leveraging function compilation to reduce the number of overall function calls. ## Download - * [Development source](https://raw.github.com/bestiejs/lodash/v0.4.2/lodash.js) - * [Production source](https://raw.github.com/bestiejs/lodash/v0.4.2/lodash.min.js) - * CDN copies of ≤ [v0.4.1](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/0.4.1/lodash.min.js) are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/) + * [Development source](https://raw.github.com/bestiejs/lodash/v0.5.0/lodash.js) + * [Production source](https://raw.github.com/bestiejs/lodash/v0.5.0/lodash.min.js) + * CDN copies of ≤ [v0.4.2](http://cdnjs.cloudflare.com/ajax/libs/lodash.js/0.4.2/lodash.min.js) are available on [cdnjs](http://cdnjs.com/) thanks to [CloudFlare](http://www.cloudflare.com/) * For optimal performance, [create a custom build](https://github.com/bestiejs/lodash#custom-builds) with only the features you need ## Dive in @@ -24,27 +24,34 @@ For a list of upcoming features, check out our [roadmap](https://github.com/best For more information check out these screencasts over Lo-Dash: * [Introducing Lo-Dash](https://vimeo.com/44154599) - * [Optimizations and custom builds](https://vimeo.com/44154601) + * [Lo-Dash optimizations and custom builds](https://vimeo.com/44154601) * [Lo-Dash’s origin and why it’s a better utility belt](https://vimeo.com/44154600) + * [Unit testing in Lo-Dash](https://vimeo.com/45865290) ## Features * AMD loader support ([RequireJS](http://requirejs.org/), [curl.js](https://github.com/cujojs/curl), etc.) * [_.bind](http://lodash.com/docs#bind) supports *"lazy"* binding + * [_.clone](http://lodash.com/docs#clone) supports *"deep"* cloning + * [_.countBy](http://lodash.com/docs#countBy) as a compainion function for [_.groupBy](http://lodash.com/docs#groupBy) and [_.sortBy](http://lodash.com/docs#sortBy) * [_.debounce](http://lodash.com/docs#debounce)’ed functions match [_.throttle](http://lodash.com/docs#throttle)’ed functions’ return value behavior - * [_.forEach](http://lodash.com/docs#forEach) is chainable + * [_.drop](http://lodash.com/docs#drop) for the inverse functionality of [_.pick](http://lodash.com/docs#pick) + * [_.forEach](http://lodash.com/docs#forEach) is chainable and supports exiting iteration early * [_.forIn](http://lodash.com/docs#forIn) for iterating over an object’s own and inherited properties * [_.forOwn](http://lodash.com/docs#forOwn) for iterating over an object’s own properties * [_.groupBy](http://lodash.com/docs#groupBy), [_.sortedIndex](http://lodash.com/docs#sortedIndex), and [_.uniq](http://lodash.com/docs#uniq) accept a `thisArg` argument * [_.indexOf](http://lodash.com/docs#indexOf) and [_.lastIndexOf](http://lodash.com/docs#lastIndexOf) accept a `fromIndex` argument - * [_.partial](http://lodash.com/docs#partial) for more functional fun + * [_.merge](http://lodash.com/docs#merge) for a *"deep"* [_.extend](http://lodash.com/docs#extend) + * [_.partial](http://lodash.com/docs#partial) for partial application without `this` binding * [_.template](http://lodash.com/docs#template) utilizes [sourceURLs](http://www.html5rocks.com/en/tutorials/developertools/sourcemaps/#toc-sourceurl) for easier debugging + * [_.where](http://lodash.com/docs#where) for filtering collections by contained properties + * [_.zipObject](http://lodash.com/docs#zipObject) for composing objects * [_.contains](http://lodash.com/docs#contains), [_.size](http://lodash.com/docs#size), [_.toArray](http://lodash.com/docs#toArray), [and more…](http://lodash.com/docs "_.every, _.filter, _.find, _.forEach, _.groupBy, _.invoke, _.map, _.pluck, _.reduce, _.reduceRight, _.reject, _.some, _sortBy") accept strings ## Support -Lo-Dash has been tested in at least Chrome 5-20, Firefox 1.5-13, IE 6-9, Opera 9.25-12, Safari 3.0.4-5.1.7, Node.js 0.4.8-0.8.2, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC3. +Lo-Dash has been tested in at least Chrome 5-21, Firefox 1.5-13, IE 6-9, Opera 9.25-12.01, Safari 3-6, Node.js 0.4.8-0.8.7, Narwhal 0.3.2, RingoJS 0.8, and Rhino 1.7RC5. ## Custom builds @@ -52,52 +59,62 @@ Custom builds make it easy to create lightweight versions of Lo-Dash containing We handle all the method dependency and alias mapping for you. * Backbone builds, containing all methods required by Backbone, may be created using the `backbone` modifier argument. -~~~ bash +```bash lodash backbone -~~~ +``` + + * CSP builds, supporting default Content Security Policy restrictions, may be created using the `csp` modifier argument. +```bash +lodash csp +``` * Legacy builds, tailored for older browsers without [ES5 support](http://es5.github.com/), may be created using the `legacy` modifier argument. -~~~ bash +```bash lodash legacy -~~~ +``` * Mobile builds, with IE < 9 bug fixes and method compilation removed, may be created using the `mobile` modifier argument. -~~~ bash +```bash lodash mobile -~~~ +``` - * Strict builds, with `_.bindAll`, `_.extend`, and `_.defaults` in [strict mode](http://es5.github.com/#C), may be created using the `strict` modifier argument. -~~~ bash + * Strict builds, with `_.bindAll`, `_.defaults`, and `_.extend` in [strict mode](http://es5.github.com/#C), may be created using the `strict` modifier argument. +```bash lodash strict -~~~ +``` + + * Underscore builds, containing only methods included in Underscore, may be created using the `underscore` modifier argument. +```bash +lodash underscore +``` Custom builds may be created in three ways: 1. Use the `category` argument to pass the categories of methods to include in the build.
Valid categories are *"arrays"*, *"chaining"*, *"collections"*, *"functions"*, *"objects"*, and *"utilities"*. -~~~ bash +```bash lodash category=collections,functions lodash category="collections, functions" -~~~ +``` 2. Use the `exclude` argument to pass the names of methods to exclude from the build. -~~~ bash +```bash lodash exclude=union,uniq,zip lodash exclude="union, uniq, zip" -~~~ +``` 3. Use the `include` argument to pass the names of methods to include in the build. -~~~ bash +```bash lodash include=each,filter,map lodash include="each, filter, map" -~~~ +``` -All arguments, except `exclude` with `include` and `legacy` with `mobile`, may be combined. +All arguments, except `backbone` with `underscore`, `exclude` with `include`, and `legacy` with `csp`/`mobile`, may be combined. -~~~ bash +```bash lodash backbone legacy category=utilities exclude=first,last -lodash backbone mobile strict category=functions include=pick,uniq -~~~ +lodash underscore mobile strict category=functions include=pick,uniq +``` The `lodash` command-line utility is available when Lo-Dash is installed as a global package (i.e. `npm install -g lodash`). @@ -107,38 +124,38 @@ Custom builds are saved to `lodash.custom.js` and `lodash.custom.min.js`. In browsers: -~~~ html +```html -~~~ +``` Using [npm](http://npmjs.org/): -~~~ bash +```bash npm install lodash npm install -g lodash -~~~ +``` In [Node.js](http://nodejs.org/) and [RingoJS v0.8.0+](http://ringojs.org/): -~~~ js +```js var _ = require('lodash'); -~~~ +``` -In [Narwhal](http://narwhaljs.org/) and [RingoJS v0.7.0-](http://ringojs.org/): +In [RingoJS v0.7.0-](http://ringojs.org/): -~~~ js +```js var _ = require('lodash')._; -~~~ +``` In [Rhino](http://www.mozilla.org/rhino/): -~~~ js +```js load('lodash.js'); -~~~ +``` In an AMD loader like [RequireJS](http://requirejs.org/): -~~~ js +```js require({ 'paths': { 'underscore': 'path/to/lodash' @@ -147,33 +164,41 @@ require({ ['underscore'], function(_) { console.log(_.VERSION); }); -~~~ +``` -## Closed Underscore.js issues (20+) +## Resolved Underscore.js issues (20+) - * Allow iteration of objects with a `length` property [[#148](https://github.com/documentcloud/underscore/issues/148), [#154](https://github.com/documentcloud/underscore/issues/154), [#252](https://github.com/documentcloud/underscore/issues/252), [#448](https://github.com/documentcloud/underscore/issues/448), [#659](https://github.com/documentcloud/underscore/issues/659), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L364-370)] - * Ensure array-like objects with invalid `length` properties are treated like regular objects [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L315-321), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L665-679), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L921-924)] - * Ensure *"Arrays"* methods allow falsey `array` arguments [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L989-1027)] - * Ensure *"Collections"* methods allow string `collection` arguments [[#247](https://github.com/documentcloud/underscore/issues/247), [#276](https://github.com/documentcloud/underscore/issues/276), [#561](https://github.com/documentcloud/underscore/pull/561), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L148-157), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L323-341), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L681-698), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L926-929)] - * Ensure templates compiled with errors are inspectable [[#666](https://github.com/documentcloud/underscore/issues/666), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L795-802)] - * Fix cross-browser object iteration bugs [[#376](https://github.com/documentcloud/underscore/issues/376), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L224-236), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L375-400), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L496-507), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L515-517), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L535-555), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L725-727)] + * Allow iteration of objects with a `length` property [[#148](https://github.com/documentcloud/underscore/issues/148), [#154](https://github.com/documentcloud/underscore/issues/154), [#252](https://github.com/documentcloud/underscore/issues/252), [#448](https://github.com/documentcloud/underscore/issues/448), [#659](https://github.com/documentcloud/underscore/issues/659), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L544-550)] + * Ensure array-like objects with invalid `length` properties are treated like regular objects [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L491-501)] + * Ensure *"Arrays"* methods allow falsey `array` arguments [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1487-1525)] + * Ensure *"Collections"* methods allow string `collection` arguments [[#247](https://github.com/documentcloud/underscore/issues/247), [#276](https://github.com/documentcloud/underscore/issues/276), [#561](https://github.com/documentcloud/underscore/pull/561), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L503-521)] + * Ensure templates compiled with errors are inspectable [[#666](https://github.com/documentcloud/underscore/issues/666), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1232-1235)] + * Fix cross-browser object iteration bugs [[#60](https://github.com/documentcloud/underscore/issues/60), [#376](https://github.com/documentcloud/underscore/issues/376), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L555-580)] * Handle arrays with `undefined` values correctly in IE < 9 [[#601](https://github.com/documentcloud/underscore/issues/601)] - * Methods should work on pages with incorrectly shimmed native methods [[#7](https://github.com/documentcloud/underscore/issues/7), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L96-102)] - * Register as an AMD module, but still export to global [[#431](https://github.com/documentcloud/underscore/pull/431), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L80-94)] - * `_(…)` should return passed wrapper instances [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L114-117)] - * `_.contains` should work with strings [[#667](https://github.com/documentcloud/underscore/pull/667), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L148-157)] - * `_.escape` should return an empty string when passed `null` or `undefined` [[#407](https://github.com/documentcloud/underscore/issues/427), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L205-208)] - * `_.forEach` should be chainable [[#142](https://github.com/documentcloud/underscore/issues/142), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L310-313)] - * `_.groupBy` should add values to own, not inherited, properties [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L415-422)] - * `_isNaN(new Number(NaN))` should return `true` [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L525-527)] - * `_.reduceRight` should pass correct callback arguments when iterating objects [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L649-663)] - * `_.size` should return the `length` of string values [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L708-710)] - * `_.size` shouldn't error on falsey values [[#650](https://github.com/documentcloud/underscore/pull/650), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L712-719)] - * `_.size` should work with `arguments` objects cross-browser [[#653](https://github.com/documentcloud/underscore/issues/653), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L721-723)] - * `_.sortedIndex` should support arrays with high `length` values [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L765-774)] - * `_.template` should not augment the `options` object [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L789-793)] - * `_.throttle` should work when called in a loop [[#502](https://github.com/documentcloud/underscore/issues/502), [test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L872-882)] - * `_.zipObject` should accept less than two arguments [[test](https://github.com/bestiejs/lodash/blob/v0.4.2/test/test.js#L951-953)] + * Methods should work on pages with incorrectly shimmed native methods [[#7](https://github.com/documentcloud/underscore/issues/7), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L117-123)] + * Register as an AMD module, but still export to global [[#431](https://github.com/documentcloud/underscore/pull/431), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L101-115)] + * `_(…)` should return passed wrapper instances [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L135-138)] + * `_.clone` should allow `deep` cloning [[#595](https://github.com/documentcloud/underscore/pull/595), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L197-212)] + * `_.contains` should work with strings [[#667](https://github.com/documentcloud/underscore/pull/667), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L267-276)] + * `_.escape` should return an empty string when passed `null` or `undefined` [[#427](https://github.com/documentcloud/underscore/issues/427), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L367-370)] + * `_.extend` should recursively extend objects [[#379](https://github.com/documentcloud/underscore/pull/379), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L882-955)] + * `_.forEach` should be chainable [[#142](https://github.com/documentcloud/underscore/issues/142), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L486-489)] + * `_.forEach` should allow exiting iteration early [[#211](https://github.com/documentcloud/underscore/issues/211), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L582-601)] + * `_.groupBy` should add values to own, not inherited, properties [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L616-623)] + * `_.isEmpty` and `_.size` should support jQuery/MooTools DOM query collections [[#690](https://github.com/documentcloud/underscore/pull/690), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L716-721)] + * `_.isEqual` should return `true` for like-objects from different documents [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L757-777)] + * `_.isObject` should avoid V8 bug [#2291](http://code.google.com/p/v8/issues/detail?id=2291) [[#605](https://github.com/documentcloud/underscore/issues/605), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L785-797)] + * `_.isNaN(new Number(NaN))` should return `true` [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L805-807)] + * `_.keys` and `_.size` should work with `arguments` objects cross-browser [[#396](https://github.com/documentcloud/underscore/issues/396), [#653](https://github.com/documentcloud/underscore/issues/653), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L817-819)] + * `_.once` should free the given function for garbage collection [[#693](https://github.com/documentcloud/underscore/pull/693)] + * `_.range` should coerce arguments to numbers [[#634](https://github.com/documentcloud/underscore/issues/634), [#683](https://github.com/documentcloud/underscore/issues/683), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1042-1045)] + * `_.reduceRight` should pass correct callback arguments when iterating objects [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1053-1067)] + * `_.size` should return the `length` of string values [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1112-1114)] + * `_.size` shouldn’t error on falsey values [[#650](https://github.com/documentcloud/underscore/pull/650), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1116-1123)] + * `_.sortedIndex` should support arrays with high `length` values [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1202-1211)] + * `_.template` should not augment the `options` object [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1226-1230)] + * `_.throttle` should work when called in a loop [[#502](https://github.com/documentcloud/underscore/issues/502), [test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1322-1332)] + * `_.zipObject` should accept less than two arguments [[test](https://github.com/bestiejs/lodash/blob/v0.5.0/test/test.js#L1449-1451)] ## Optimized methods (50+) @@ -201,7 +226,6 @@ require({ * `_.isArguments` * `_.isDate` * `_.isEmpty` - * `_.isEqual` * `_.isFinite` * `_.isFunction` * `_.isObject` @@ -238,33 +262,24 @@ require({ ## Release Notes -### v0.4.2 - - * Added `strict` build - * Ensured `_.bindAll`, `_.extend`, and `_.defaults` avoid strict mode errors when attempting to augment read-only properties - * Optimized the iteration of large arrays in `_.difference`, `_.intersection`, and `_.without` - * Fixed build bugs related to removing variables - -### v0.4.1 - - * Fixed `_.template` regression - * Optimized build process to detect and remove more unused variables - -### v0.4.0 - - * Added `bin` and `scripts` entries to package.json - * Added `legacy` build option - * Added cross-browser support for passing strings to *"Collections"* methods - * Added `_.zipObject` - * Leveraged `_.indexOf`'s `fromIndex` in `_.difference` and `_.without` - * Optimized compiled templates - * Optimized inlining the `iteratorTemplate` for builds - * Optimized object iteration for *"Collections"* methods - * Optimized partially applied `_.bind` in V8 - * Made compiled templates more debuggable - * Made `_.size` work with falsey values and consistent cross-browser with `arguments` objects - * Moved `_.groupBy` and `_.sortBy` back to the *"Collections"* category - * Removed `arguments` object from `_.range` +### v0.5.0 + + * Added [_.countBy](http://lodash.com/docs#countBy), [_.drop](http://lodash.com/docs#drop), [_.merge](http://lodash.com/docs#merge), and [_.where](http://lodash.com/docs#where) + * Added `csp` *(Content Security Policy)* and `underscore` build options + * Added `deep` cloning support to `_.clone` + * Added [Jam](http://jamjs.org/) package support + * Added support for exiting `_.forEach`, `_.forIn`, and `_.forOwn` early by returning `false` in the `callback` + * Added support for jQuery/MooTools DOM query collections to `_.isEmpty` and `_.size` + * Ensured development build works with IE conditional compilation enabled + * Ensured `_.clone` doesn't clone functions, DOM nodes, `arguments` objects, and objects created by constructors other than `Object` + * Ensured `_.filter`’s `callback` can't modify result values + * Ensured `_.isEmpty`, `_.isEquals`, and `_.size` support `arguments` objects + * Ensured `_.isEqual` doesn't inspect DOM nodes, works with objects from other documents, and calls custom `isEqual` methods before checking strict equality + * Ensured `_.once` frees the given function for garbage collection + * Ensured `_.sortBy` performs a stable sort + * Ensured `reEvaluateDelimiter` is assigned when `_.templateSettings.evaluate` is undefined + * Made `_.range` coerce arguments to numbers + * Optimized `_.isFunction` The full changelog is available [here](https://github.com/bestiejs/lodash/wiki/Changelog). diff --git a/build.js b/build.js index 9b34b18369..8be41afc69 100755 --- a/build.js +++ b/build.js @@ -11,14 +11,17 @@ /** The current working directory */ var cwd = process.cwd(); - /** Flag used to specify a backbone build */ + /** Flag used to specify a Backbone build */ var isBackbone = process.argv.indexOf('backbone') > -1; + /** Flag used to specify a Content Security Policy build */ + var isCSP = process.argv.indexOf('csp') > -1 || process.argv.indexOf('CSP') > -1; + /** Flag used to specify a legacy build */ var isLegacy = process.argv.indexOf('legacy') > -1; /** Flag used to specify a mobile build */ - var isMobile = !isLegacy && process.argv.indexOf('mobile') > -1; + var isMobile = !isLegacy && (isCSP || process.argv.indexOf('mobile') > -1); /** * Flag used to specify `_.bindAll`, `_.extend`, and `_.defaults` are @@ -26,6 +29,9 @@ */ var isStrict = process.argv.indexOf('strict') > -1; + /** Flag used to specify an Underscore build */ + var isUnderscore = process.argv.indexOf('underscore') > -1; + /** Flag used to specify if the build should include the "use strict" directive */ var useStrict = isStrict || !(isLegacy || isMobile); @@ -48,6 +54,7 @@ } if (isLegacy) { + source = replaceVar(source, 'noArgsClass', 'true'); ['isBindFast', 'isKeysFast', 'nativeBind', 'nativeIsArray', 'nativeKeys'].forEach(function(varName) { source = replaceVar(source, varName, 'false'); }); @@ -146,15 +153,17 @@ 'bind': [], 'bindAll': ['bind', 'functions'], 'chain': ['mixin'], - 'clone': ['extend', 'isArray'], + 'clone': ['extend', 'forIn', 'forOwn', 'isArguments'], 'compact': [], 'compose': [], 'contains': [], + 'countBy': [], 'debounce': [], 'defaults': [], 'defer': [], 'delay': [], 'difference': ['indexOf'], + 'drop': ['indexOf'], 'escape': [], 'every': ['identity'], 'extend': [], @@ -178,8 +187,8 @@ 'isBoolean': [], 'isDate': [], 'isElement': [], - 'isEmpty': [], - 'isEqual': [], + 'isEmpty': ['isArguments'], + 'isEqual': ['isArguments'], 'isFinite': [], 'isFunction': [], 'isNaN': [], @@ -195,6 +204,7 @@ 'map': ['identity'], 'max': [], 'memoize': [], + 'merge': ['isArray', 'forIn'], 'min': [], 'mixin': ['forEach', 'functions'], 'noConflict': [], @@ -209,7 +219,7 @@ 'rest': [], 'result': [], 'shuffle': [], - 'size': ['keys'], + 'size': ['isArguments', 'keys'], 'some': ['identity'], 'sortBy': [], 'sortedIndex': ['bind'], @@ -222,6 +232,7 @@ 'uniq': ['identity', 'indexOf'], 'uniqueId': [], 'values': [], + 'where': ['forIn'], 'without': ['indexOf'], 'wrap': [], 'zip': ['max', 'pluck'], @@ -243,6 +254,7 @@ 'isKeysFast', 'object', 'objectBranch', + 'noArgsEnum', 'noCharByIndex', 'shadowed', 'top', @@ -251,10 +263,21 @@ ]; /** Collections of method names */ - var excludeMethods, - includeMethods, + var excludeMethods = [], + includeMethods = [], allMethods = Object.keys(dependencyMap); + var underscoreMethods = lodash.without.apply(lodash, [allMethods].concat([ + 'countBy', + 'drop', + 'forIn', + 'forOwn', + 'merge', + 'partial', + 'where', + 'zipObject' + ])); + /** Used to specify whether filtering is for exclusion or inclusion */ var filterType = process.argv.reduce(function(result, value) { if (result) { @@ -289,14 +312,17 @@ ' Commands:', '', ' lodash backbone Build containing all methods required by Backbone', + ' lodash csp Build supporting default Content Security Policy restrictions', ' lodash legacy Build tailored for older browsers without ES5 support', ' lodash mobile Build with IE < 9 bug fixes and method compilation removed', - ' lodash strict Build with `_.bindAll`, `_.extend`, and `_.defaults` in strict mode', + ' lodash strict Build with `_.bindAll`, `_.defaults`, and `_.extend` in strict mode', + ' lodash underscore Build containing only methods included in Underscore', ' lodash category=... Comma separated categories of methods to include in the build', ' lodash exclude=... Comma separated names of methods to exclude from the build', ' lodash include=... Comma separated names of methods to include in the build', '', - ' All arguments, except `exclude` with `include` and `legacy` with `mobile`, may be combined.', + ' All arguments, except `backbone` with `underscore`, `exclude` with `include`,', + ' and `legacy` with `csp`/`mobile`, may be combined.', '', ' Options:', '', @@ -317,6 +343,17 @@ return realToAliasMap[funcName] || []; } + /** + * Gets the Lo-Dash method assignments snippet from `source`. + * + * @private + * @param {String} source The source to inspect. + * @returns {String} Returns the method assignments snippet. + */ + function getMethodAssignments(source) { + return (source.match(/lodash\.VERSION *= *[\s\S]+?\/\*-+\*\/\n/) || [''])[0]; + } + /** * Gets an array of depenants for a function by a given name. * @@ -385,7 +422,7 @@ * @returns {String} Returns the `isArguments` fallback snippet. */ function getIsArgumentsFallback(source) { - return (source.match(/(?:\s*\/\/.*)*\s*if *\(!(?:lodash\.)?isArguments[^)]+\)[\s\S]+?};\s*}/) || [''])[0]; + return (source.match(/(?:\s*\/\/.*)*\s*if *\(noArgsClass[\s\S]+?};\s*}/) || [''])[0]; } /** @@ -452,7 +489,7 @@ function removeFromCreateIterator(source, refName) { var snippet = matchFunction(source, 'createIterator'); if (snippet) { - // clip the snippet the `factory` assignment + // clip the snippet at the `factory` assignment snippet = snippet.match(/Function\([\s\S]+$/)[0]; var modified = snippet.replace(RegExp('\\b' + refName + '\\b,? *', 'g'), ''); source = source.replace(snippet, modified); @@ -481,7 +518,7 @@ source = source.replace(matchFunction(source, funcName), ''); // grab the method assignments snippet - snippet = source.match(/lodash\.VERSION *= *[\s\S]+?\/\*-+\*\/\n/)[0]; + snippet = getMethodAssignments(source); // remove assignment and aliases modified = getAliases(funcName).concat(funcName).reduce(function(result, otherName) { @@ -524,6 +561,38 @@ .replace(/\s*.+?\.isKeysFast *=.+/, ''); } + /** + * Removes all `noArgsClass` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeNoArgsClass(source) { + return removeVar(source, 'noArgsClass') + // remove `noArgsClass` from `_.clone`, `_.isEqual`, and `_.size` + .replace(/ *\|\| *\(noArgsClass *&&[^)]+?\)\)/g, '') + // remove `noArgsClass` from `_.isEqual` + .replace(/if *\(noArgsClass[^}]+?}\n/, ''); + } + + /** + * Removes all `noNodeClass` references from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeNoNodeClass(source) { + return source + // remove `noNodeClass` assignment + .replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*)*\n *var noNodeClass[\s\S]+?catch[^}]+}\n/, '') + // remove `noNodeClass` from `isPlainObject` + .replace(/\(!noNodeClass *\|\|[\s\S]+?\)\) *&&/, '') + // remove `noNodeClass` from `_.isEqual` + .replace(/ *\|\| *\(noNodeClass *&&[\s\S]+?\)\)\)/, ''); + } + /** * Removes the "use strict" directive from `source`. * @@ -563,6 +632,9 @@ // remove a variable at the end of a variable declaration list source = source.replace(RegExp(',\\s*' + varName + ' *=.+?;'), ';'); + // remove variable reference from `arrayLikeClasses` and `cloneableClasses` assignments + source = source.replace(RegExp('(?:arrayLikeClasses|cloneableClasses)\\[' + varName + '\\] *= *(?:false|true)?', 'g'), ''); + return removeFromCreateIterator(source, varName); } @@ -609,6 +681,25 @@ .replace(/\s*.+?\.useStrict *=.+/, ''); } + /** + * Writes `source` to a file with the given `filename` to the current + * working directory. + * + * @private + * @param {String} source The source to write. + * @param {String} filename The name of the file. + */ + function writeFile(source, filename) { + // correct overly aggressive Closure Compiler minification + source = source.replace(/prototype\s*=\s*{\s*valueOf\s*:\s*1\s*}/, 'prototype={valueOf:1,y:1}'); + + // re-remove "use strict" added by the minifier + if (!useStrict) { + source = removeUseStrictDirective(source); + } + fs.writeFileSync(path.join(cwd, filename), source); + } + /*--------------------------------------------------------------------------*/ // display help message @@ -629,25 +720,47 @@ /*--------------------------------------------------------------------------*/ - // Backbone build - if (isBackbone) { + // don't expose `_.forIn` or `_.forOwn` if `isUnderscore` is `true` unless + // requested by `include` + if (isUnderscore) { + if (includeMethods.indexOf('forIn') == -1) { + source = source.replace(/ *lodash\.forIn *=.+\n/, ''); + } + if (includeMethods.indexOf('forOwn') == -1) { + source = source.replace(/ *lodash\.forOwn *=.+\n/, ''); + } + } + + // add methods required by Backbone or Underscore + [ + { 'flag': isBackbone, 'methodNames': backboneDependencies }, + { 'flag': isUnderscore, 'methodNames': underscoreMethods } + ] + .some(function(data) { + var flag = data.flag, + methodNames = data.methodNames; + + if (!flag) { + return false; + } // add any additional sub-dependencies - backboneDependencies = getDependencies(backboneDependencies); + methodNames = getDependencies(methodNames); if (filterType == 'exclude') { - // remove excluded methods from `backboneDependencies` - includeMethods = lodash.without.apply(lodash, [backboneDependencies].concat(excludeMethods)); + // remove excluded methods from `methodNames` + includeMethods = lodash.without.apply(lodash, [methodNames].concat(excludeMethods)); } else if (filterType) { - // merge backbone dependencies into `includeMethods` - includeMethods = lodash.union(includeMethods, backboneDependencies); + // merge `methodNames` into `includeMethods` + includeMethods = lodash.union(includeMethods, methodNames); } else { - // include only the Backbone dependencies - includeMethods = backboneDependencies; + // include only the `methodNames` + includeMethods = methodNames; } filterType = 'include'; - } + return true; + }); /*--------------------------------------------------------------------------*/ @@ -717,14 +830,13 @@ /*--------------------------------------------------------------------------*/ - // simplify template snippets + // simplify template snippets by removing unnecessary brackets source = source.replace( - RegExp( - "'else if \\(thisArg\\) \\{\\\\n' \\+\\s*" + - "' callback = iteratorBind\\(callback, thisArg\\)\\\\n' \\+\\s*" + - "'}'" - , 'g'), - "'else if (thisArg) callback = iteratorBind(callback, thisArg)'" + RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *([,\\n])", 'g'), "$1'$2" + ); + + source = source.replace( + RegExp("{(\\\\n' *\\+\\s*.*?\\+\\n\\s*' *)}(?:\\\\n)?' *\\+", 'g'), "$1;\\n'+" ); /*--------------------------------------------------------------------------*/ @@ -739,32 +851,20 @@ if (!iteratorName) { return; } - var snippet, - funcNames = [], - objectSnippets = [], - token = '__isTypeToken__'; + var funcNames = [], + objectSnippets = []; // build replacement code lodash.forOwn({ - 'Arguments': "'[object Arguments]'", 'Date': 'dateClass', - 'Function': 'funcClass', 'Number': 'numberClass', 'RegExp': 'regexpClass', 'String': 'stringClass' }, function(value, key) { - // skip `isArguments` if a legacy build - if (isLegacy && key == 'Arguments') { - return; - } var funcName = 'is' + key, funcCode = matchFunction(source, funcName); if (funcCode) { - if (!snippet) { - // use snippet to mark the insert position - snippet = funcCode; - } funcNames.push(funcName); objectSnippets.push("'" + key + "': " + value); } @@ -774,30 +874,22 @@ if (funcNames.length < 2) { return; } - // add a token to mark the position to insert new code - source = source.replace(snippet, '\n' + token + '\n' + snippet); // remove existing isType functions funcNames.forEach(function(funcName) { source = removeFunction(source, funcName); }); - // replace token with new DRY code - source = source.replace(token, + // insert new DRY code after the method assignments + var snippet = getMethodAssignments(source); + source = source.replace(snippet, snippet + '\n' + ' // add `_.' + funcNames.join('`, `_.') + '`\n' + ' ' + iteratorName + '({\n ' + objectSnippets.join(',\n ') + '\n }, function(className, key) {\n' + " lodash['is' + key] = function(value) {\n" + ' return toString.call(value) == className;\n' + ' };\n' + - ' });' + ' });\n' ); - - // tweak `isArguments` fallback - snippet = !isLegacy && getIsArgumentsFallback(source); - if (snippet) { - var modified = '\n' + snippet.replace(/isArguments/g, 'lodash.$&'); - source = source.replace(snippet, modified); - } }()); /*--------------------------------------------------------------------------*/ @@ -812,11 +904,11 @@ modified = snippet; // remove native `Function#bind` branch in `_.bind` - if (funcName == 'bind' ) { + if (funcName == 'bind') { modified = modified.replace(/(?:\s*\/\/.*)*\s*else if *\(isBindFast[^}]+}/, ''); } // remove native `Array.isArray` branch in `_.isArray` - else if (funcName == 'isArray') { + else { modified = modified.replace(/nativeIsArray * \|\|/, ''); } source = source.replace(snippet, modified); @@ -834,8 +926,8 @@ // replace `_.isArguments` with fallback if (!isRemoved(source, 'isArguments')) { source = source.replace( - matchFunction(source, 'isArguments').replace(/[\s\S]+?var isArguments *=/, ''), - getIsArgumentsFallback(source).match(/isArguments *=([\s\S]+?) *};/)[1] + ' };\n' + matchFunction(source, 'isArguments').replace(/[\s\S]+?function isArguments/, ''), + getIsArgumentsFallback(source).match(/isArguments *= *function([\s\S]+?) *};/)[1] + ' }\n' ); source = removeIsArgumentsFallback(source); @@ -860,25 +952,37 @@ source = source.replace(reFunc, '$1' + getFunctionSource(lodash[funcName]) + ';\n'); }); + // replace `callee` in `_.merge` with `merge` + source = source.replace(matchFunction(source, 'merge'), function(match) { + return match.replace(/\bcallee\b/g, 'merge'); + }); + + // remove `hasDontEnumBug`, `iteratesOwnLast`, `noArgsEnum` assignment + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug\b[\s\S]+?}\(1\)\);\n/, ''); + + // remove `iteratesOwnLast` from `isPlainObject` + source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(iteratesOwnLast[\s\S]+?\n\1}/, ''); + // remove JScript [[DontEnum]] fix from `_.isEqual` - source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(result *&& *hasDontEnumBug[\s\S]+?\n\1}/, ''); + source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(hasDontEnumBug[\s\S]+?\n\1}/, ''); // remove IE `shift` and `splice` fix from mutator Array functions mixin source = source.replace(/(?:\s*\/\/.*)*\n( +)if *\(value.length *=== *0[\s\S]+?\n\1}/, ''); - // remove `noCharByIndex` from `_.reduceRight` - source = source.replace(/noCharByIndex *&&[^:]+: *([^;]+)/g, '$1'); - // remove `noArraySliceOnStrings` from `_.toArray` source = source.replace(/noArraySliceOnStrings *\?[^:]+: *([^)]+)/g, '$1'); + // remove `noCharByIndex` from `_.reduceRight` + source = source.replace(/noCharByIndex *&&[^:]+: *([^;]+)/g, '$1'); + source = removeVar(source, 'extendIteratorOptions'); - source = removeVar(source, 'hasDontEnumBug'); source = removeVar(source, 'iteratorTemplate'); source = removeVar(source, 'noArraySliceOnStrings'); source = removeVar(source, 'noCharByIndex'); source = removeIsArgumentsFallback(source); source = removeKeysOptimization(source); + source = removeNoArgsClass(source); + source = removeNoNodeClass(source); } else { // inline `iteratorTemplate` template @@ -937,9 +1041,6 @@ // remove `LoDash.prototype` additions source = source.replace(/(?:\s*\/\/.*)*\s*LoDash.prototype *=[\s\S]+?\/\*-+\*\//, ''); } - if (isRemoved(source, 'sortBy')) { - source = removeFunction(source, 'compareAscending'); - } if (isRemoved(source, 'template')) { // remove `templateSettings` assignment source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *lodash\.templateSettings[\s\S]+?};\n/, ''); @@ -947,32 +1048,34 @@ if (isRemoved(source, 'toArray')) { source = removeVar(source, 'noArraySliceOnStrings'); } - if (isRemoved(source, 'isArray', 'isEmpty', 'isEqual')) { - source = removeVar(source, 'arrayClass'); + if (isRemoved(source, 'clone', 'merge')) { + source = removeFunction(source, 'isPlainObject'); } - if (isRemoved(source, 'bind', 'bindAll', 'functions', 'isEqual', 'isFunction', 'result', 'toArray')) { - source = removeVar(source, 'funcClass'); + if (isRemoved(source, 'clone', 'isArguments', 'isEmpty', 'isEqual', 'size')) { + source = removeNoArgsClass(source); } - if (isRemoved(source, 'bind', 'clone', 'isObject', 'keys')) { - source = removeVar(source, 'objectTypes'); + if (isRemoved(source, 'isEqual', 'isPlainObject')) { + source = removeNoNodeClass(source); } if ((source.match(/\bcreateIterator\b/g) || []).length < 2) { source = removeFunction(source, 'createIterator'); + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var noArgsEnum;|.+?noArgsEnum *=.+/g, ''); } if (isRemoved(source, 'createIterator', 'bind', 'isArray', 'keys')) { source = removeVar(source, 'reNative'); } - if (isRemoved(source, 'createIterator', 'extend', 'isEqual')) { - source = removeVar(source, 'hasDontEnumBug'); + if (isRemoved(source, 'createIterator', 'clone', 'merge')) { + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var iteratesOwnLast;|.+?iteratesOwnLast *=.+/g, ''); } - if (isRemoved(source, 'createIterator', 'contains', 'isEmpty', 'isEqual', 'isString')) { - source = removeVar(source, 'stringClass'); + if (isRemoved(source, 'createIterator', 'isEqual')) { + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *var hasDontEnumBug;|.+?hasDontEnumBug *=.+/g, ''); } if (isRemoved(source, 'createIterator', 'keys')) { source = removeVar(source, 'nativeKeys'); } - if (isRemoved(source, 'createIterator', 'reduceRight')) { - source = removeVar(source, 'noCharByIndex'); + if (!source.match(/var (?:hasDontEnumBug|iteratesOwnLast|noArgsEnum)\b/g)) { + // remove `hasDontEnumBug`, `iteratesOwnLast`, `noArgsEnum` assignment + source = source.replace(/ *\(function\(\) *{\s*var props\b[\s\S]+?}\(1\)\);/, ''); } // remove pseudo private properties @@ -987,20 +1090,16 @@ source = source.replace(/^ *;\n/gm, ''); // begin the minification process - if (filterType || isBackbone || isLegacy || isMobile || isStrict) { - fs.writeFileSync(path.join(cwd, 'lodash.custom.js'), source); + if (filterType || isBackbone || isLegacy || isMobile || isStrict || isUnderscore) { + writeFile(source, 'lodash.custom.js'); minify(source, 'lodash.custom.min', function(result) { - // re-remove "use strict" added by the minifier - if (!useStrict) { - result = removeUseStrictDirective(result); - } - fs.writeFileSync(path.join(cwd, 'lodash.custom.min.js'), result); + writeFile(result, 'lodash.custom.min.js'); }); } else { minify(source, 'lodash.min', function(result) { - fs.writeFileSync(path.join(cwd, 'lodash.min.js'), result); + writeFile(result, 'lodash.min.js'); }); } }()); diff --git a/build/post-compile.js b/build/post-compile.js index 2db1732277..4bd6d1999e 100644 --- a/build/post-compile.js +++ b/build/post-compile.js @@ -38,7 +38,7 @@ source = source.replace(/(\w)\["([^."]+)"\]/g, '$1.$2'); // correct AMD module definition for AMD build optimizers - source = source.replace(/("function")==(typeof define)&&\(?("object")==(typeof define\.amd)(&&define\.amd)\)?/, '$2==$1&&$4==$3$5'); + source = source.replace(/("function")\s*==\s*(typeof define)\s*&&\s*\(?\s*("object")\s*==\s*(typeof define\.amd)\s*&&\s*(define\.amd)\s*\)?/, '$2==$1&&$4==$3&&$5'); // add license source = license + '\n;' + source; diff --git a/build/pre-compile.js b/build/pre-compile.js index 7e0c049447..069f770d22 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -7,43 +7,69 @@ /** Used to minify variables embedded in compiled strings */ var compiledVars = [ - 'accumulator', - 'args', - 'arrayClass', - 'bind', 'callback', - 'className', 'collection', - 'compareAscending', + 'concat', 'ctor', - 'funcClass', - 'funcs', 'hasOwnProperty', 'identity', 'index', - 'isFunc', 'iteratee', - 'iterateeIndex', 'iteratorBind', 'length', - 'methodName', 'nativeKeys', - 'noaccum', 'object', - 'objectTypes', + 'ownIndex', + 'ownProps', 'prop', + 'propertyIsEnumerable', 'propIndex', 'props', - 'property', - 'propertyIsEnumerable', 'result', 'skipProto', 'slice', 'stringClass', - 'target', 'thisArg', 'toString', - 'value' + 'value', + + // lesser used variables + 'accumulator', + 'args', + 'argsIndex', + 'argsLength', + 'arrayLikeClasses', + 'ArrayProto', + 'bind', + 'callee', + 'className', + 'compareAscending', + 'destValue', + 'forIn', + 'found', + 'funcs', + 'indexOf', + 'indicator', + 'isArguments', + 'isArr', + 'isArray', + 'isFunc', + 'isFunction', + 'isPlainObject', + 'methodName', + 'noaccum', + 'objectClass', + 'objectTypes', + 'pass', + 'properties', + 'property', + 'propsLength', + 'recursive', + 'source', + 'stack', + 'stackLength', + 'target', + 'valueProp' ]; /** Used to minify `compileIterator` option properties */ @@ -61,6 +87,7 @@ 'isKeysFast', 'object', 'objectBranch', + 'noArgsEnum', 'noCharByIndex', 'shadowed', 'top', @@ -70,6 +97,9 @@ /** Used to minify variables and string values to a single character */ var minNames = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); + minNames.push.apply(minNames, minNames.map(function(value) { + return value + value; + })); /** Used to protect the specified properties from getting minified */ var propWhitelist = [ @@ -90,6 +120,7 @@ 'compact', 'compose', 'contains', + 'countBy', 'criteria', 'debounce', 'defaults', @@ -97,6 +128,7 @@ 'delay', 'detect', 'difference', + 'drop', 'each', 'environment', 'escape', @@ -119,6 +151,7 @@ 'head', 'identity', 'include', + 'index', 'indexOf', 'initial', 'inject', @@ -149,6 +182,7 @@ 'map', 'max', 'memoize', + 'merge', 'methods', 'min', 'mixin', @@ -188,6 +222,7 @@ 'values', 'variable', 'VERSION', + 'where', 'without', 'wrap', 'zip', @@ -209,6 +244,22 @@ // remove unrecognized JSDoc tags so Closure Compiler won't complain source = source.replace(/@(?:alias|category)\b.*/g, ''); + // manually convert `arrayLikeClasses` property assignments because + // Closure Compiler errors trying to minify them + source = source.replace(/(arrayLikeClasses =)[\s\S]+?= *true/, + "$1{'[object Arguments]': true, '[object Array]': true, '[object Boolean]': false, " + + "'[object Date]': false, '[object Function]': false, '[object Number]': false, " + + "'[object Object]': false, '[object RegExp]': false, '[object String]': true }" + ); + + // manually convert `cloneableClasses` property assignments because + // Closure Compiler errors trying to minify them + source = source.replace(/(cloneableClasses =)[\s\S]+?= *true/, + "$1{'[object Arguments]': false, '[object Array]': true, '[object Boolean]': true, " + + "'[object Date]': true, '[object Function]': false, '[object Number]': true, " + + "'[object Object]': true, '[object RegExp]': true, '[object String]': true }" + ); + // add brackets to whitelisted properties so Closure Compiler won't mung them // http://code.google.com/closure/compiler/docs/api-tutorial3.html#export source = source.replace(RegExp('\\.(' + propWhitelist.join('|') + ')\\b', 'g'), "['$1']"); @@ -233,7 +284,7 @@ }); }); - // remove whitespace from `_.template` related regexpes + // remove whitespace from `_.template` related regexes source = source.replace(/(?:reDelimiterCode\w+|reEmptyString\w+|reInsertVariable) *=.+/g, function(match) { return match.replace(/ |\\n/g, ''); }); @@ -247,35 +298,59 @@ .replace("') {\\n'", "'){'") // remove `useSourceURL` variable - source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*\n)* *var useSourceURL[\s\S]+?catch[^}]+}\n/, ''); + source = source.replace(/(?:\n +\/\*[^*]*\*+(?:[^\/][^*]*\*+)*\/)?\n *try *\{(?:\s*\/\/.*)*\n *var useSourceURL[\s\S]+?catch[^}]+}\n/, ''); // remove debug sourceURL use in `_.template` source = source.replace(/(?:\s*\/\/.*\n)* *if *\(useSourceURL[^}]+}/, ''); - // minify internal properties used by `_.sortBy` + // minify internal properties used by 'compareAscending', `_.clone`, `_.merge`, and `_.sortBy` (function() { - var properties = ['criteria', 'value'], - snippets = source.match(/( +)(?:function compareAscending|var sortBy)\b[\s\S]+?\n\1}/g); + var properties = ['criteria', 'index', 'source', 'value'], + snippets = source.match(/( +)(?:function clone|function compareAscending|var merge|var sortBy)\b[\s\S]+?\n\1}/g); if (!snippets) { return; } snippets.forEach(function(snippet) { var modified = snippet, - isSortBy = /var sortBy\b/.test(modified), + isCompilable = /(?:var merge|var sortBy)\b/.test(modified), isInlined = !/\bcreateIterator\b/.test(modified); // minify properties properties.forEach(function(property, index) { - // add quotes around properties in the inlined `_.sortBy` of the mobile - // build so Closure Compiler won't mung them - if (isSortBy && isInlined) { + var reBracketProp = RegExp("\\['(" + property + ")'\\]", 'g'), + reDotProp = RegExp('\\.' + property + '\\b', 'g'), + rePropColon = RegExp("(')?\\b" + property + "\\1 *:", 'g'); + + if (isCompilable) { + // add quotes around properties in the inlined `_.merge` and `_.sortBy` + // of the mobile build so Closure Compiler won't mung them + if (isInlined) { + modified = modified + .replace(reBracketProp, "['" + minNames[index] + "']") + .replace(reDotProp, "['" + minNames[index] + "']") + .replace(rePropColon, "'" + minNames[index] + "':"); + } + else { + modified = modified + .replace(reBracketProp, '.' + minNames[index]) + .replace(reDotProp, '.' + minNames[index]) + .replace(rePropColon, minNames[index] + ':'); + } + } + else { modified = modified - .replace(RegExp('\\.' + property + '\\b', 'g'), "['" + minNames[index] + "']") - .replace(RegExp('\\b' + property + ' *:', 'g'), "'" + minNames[index] + "':"); + .replace(reBracketProp, "['" + minNames[index] + "']") + .replace(reDotProp, '.' + minNames[index]) + .replace(rePropColon, "'" + minNames[index] + "':") + + // correct `value.source` in regexp branch of `_.clone` + if (property == 'source') { + modified = modified.replace("value['" + minNames[index] + "']", "value['source']"); + } } - modified = modified.replace(RegExp('\\b' + property + '\\b', 'g'), minNames[index]); }); + // replace with modified snippet source = source.replace(snippet, modified); }); @@ -340,7 +415,7 @@ else { // minify property name strings modified = modified.replace(RegExp("'" + property + "'", 'g'), "'" + minNames[index] + "'"); - // minify property names in regexps and accessors + // minify property names in regexes and accessors if (isCreateIterator) { modified = modified.replace(RegExp('([\\.|/])' + property + '\\b' , 'g'), '$1' + minNames[index]); } diff --git a/doc/README.md b/doc/README.md index 75930cb596..7b84aea648 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,26 +1,28 @@ -# Lo-Dash v0.4.2 +# Lo-Dash v0.5.0 -## `_` +## `_` * [`_`](#_value) * [`_.VERSION`](#_version) * [`_.after`](#_aftern-func) * [`_.bind`](#_bindfunc--thisarg-arg1-arg2-) * [`_.bindAll`](#_bindallobject--methodname1-methodname2-) * [`_.chain`](#_chainvalue) -* [`_.clone`](#_clonevalue) +* [`_.clone`](#_clonevalue-deep--guard-stack-thorough) * [`_.compact`](#_compactarray) * [`_.compose`](#_composefunc1-func2-) * [`_.contains`](#_containscollection-target) +* [`_.countBy`](#_countbycollection-callback--thisarg) * [`_.debounce`](#_debouncefunc-wait-immediate) -* [`_.defaults`](#_defaultsobject--defaults1-defaults2-) +* [`_.defaults`](#_defaultsobject--default1-default2-) * [`_.defer`](#_deferfunc--arg1-arg2-) * [`_.delay`](#_delayfunc-wait--arg1-arg2-) * [`_.difference`](#_differencearray--array1-array2-) +* [`_.drop`](#_dropobject--prop1-prop2-) * [`_.escape`](#_escapestring) * [`_.every`](#_everycollection--callbackidentity-thisarg) * [`_.extend`](#_extendobject--source1-source2-) @@ -45,7 +47,7 @@ * [`_.isDate`](#_isdatevalue) * [`_.isElement`](#_iselementvalue) * [`_.isEmpty`](#_isemptyvalue) -* [`_.isEqual`](#_isequala-b--stack) +* [`_.isEqual`](#_isequala-b--stack-thorough) * [`_.isFinite`](#_isfinitevalue) * [`_.isFunction`](#_isfunctionvalue) * [`_.isNaN`](#_isnanvalue) @@ -61,6 +63,7 @@ * [`_.map`](#_mapcollection--callbackidentity-thisarg) * [`_.max`](#_maxarray--callback-thisarg) * [`_.memoize`](#_memoizefunc--resolver) +* [`_.merge`](#_mergeobject--source1-source2--indicator-stack) * [`_.min`](#_minarray--callback-thisarg) * [`_.mixin`](#_mixinobject) * [`_.noConflict`](#_noconflict) @@ -88,10 +91,11 @@ * [`_.uniq`](#_uniqarray--issortedfalse-callbackidentity-thisarg) * [`_.uniqueId`](#_uniqueidprefix) * [`_.values`](#_valuesobject) +* [`_.where`](#_wherecollection-properties) * [`_.without`](#_withoutarray--value1-value2-) -* [`_.wrap`](#_wrapfunc-wrapper--arg1-arg2-) +* [`_.wrap`](#_wrapvalue-wrapper) * [`_.zip`](#_ziparray1-array2-) -* [`_.zipObject`](#_zipobjectkeys) +* [`_.zipObject`](#_zipobjectkeys--values) @@ -130,7 +134,7 @@ ### `_(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L192 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L249 "View in source") [Ⓣ][1] The `lodash` function. @@ -148,7 +152,7 @@ The `lodash` function. ### `_.VERSION` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3608 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4046 "View in source") [Ⓣ][1] *(String)*: The semantic version number. @@ -160,7 +164,7 @@ The `lodash` function. ### `_.after(n, func)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1985 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3145 "View in source") [Ⓣ][1] Creates a new function that is restricted to executing only after it is called `n` times. @@ -172,13 +176,13 @@ Creates a new function that is restricted to executing only after it is called ` *(Function)*: Returns the new restricted function. #### Example -~~~ js +```js var renderNotes = _.after(notes.length, render); _.forEach(notes, function(note) { note.asyncSave({ 'success': renderNotes }); }); // `renderNotes` is run once, after all notes have saved -~~~ +``` * * * @@ -188,7 +192,7 @@ _.forEach(notes, function(note) { ### `_.bind(func [, thisArg, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2039 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3199 "View in source") [Ⓣ][1] Creates a new function that, when called, invokes `func` with the `this` binding of `thisArg` and prepends any additional `bind` arguments to those passed to the bound function. Lazy defined methods may be bound by passing the object they are bound to as `func` and the method name as `thisArg`. @@ -201,7 +205,7 @@ Creates a new function that, when called, invokes `func` with the `this` binding *(Function)*: Returns the new bound function. #### Example -~~~ js +```js // basic bind var func = function(greeting) { return greeting + ' ' + this.name; @@ -229,7 +233,7 @@ object.greet = function(greeting) { func(); // => 'hi, moe!' -~~~ +``` * * * @@ -239,7 +243,7 @@ func(); ### `_.bindAll(object [, methodName1, methodName2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2109 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3269 "View in source") [Ⓣ][1] Binds methods on `object` to `object`, overwriting the existing method. If no method names are provided, all the function properties of `object` will be bound. @@ -248,10 +252,10 @@ Binds methods on `object` to `object`, overwriting the existing method. If no me 2. `[methodName1, methodName2, ...]` *(String)*: Method names on the object to bind. #### Returns -*(Object)*: Returns the `object`. +*(Object)*: Returns `object`. #### Example -~~~ js +```js var buttonView = { 'label': 'lodash', 'onClick': function() { alert('clicked: ' + this.label); } @@ -260,7 +264,7 @@ var buttonView = { _.bindAll(buttonView); jQuery('#lodash_button').on('click', buttonView.onClick); // => When the button is clicked, `this.label` will have the correct value -~~~ +``` * * * @@ -270,7 +274,7 @@ jQuery('#lodash_button').on('click', buttonView.onClick); ### `_.chain(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3533 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3971 "View in source") [Ⓣ][1] Wraps the value in a `lodash` wrapper object. @@ -281,7 +285,7 @@ Wraps the value in a `lodash` wrapper object. *(Object)*: Returns the wrapper object. #### Example -~~~ js +```js var stooges = [ { 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }, @@ -294,7 +298,7 @@ var youngest = _.chain(stooges) .first() .value(); // => 'moe is 40' -~~~ +``` * * * @@ -303,22 +307,40 @@ var youngest = _.chain(stooges) -### `_.clone(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2439 "View in source") [Ⓣ][1] +### `_.clone(value, deep [, guard, stack=[]], thorough)` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L994 "View in source") [Ⓣ][1] -Create a shallow clone of the `value`. Any nested objects or arrays will be assigned by reference and not cloned. +Creates a clone of `value`. If `deep` is `true`, all nested objects will also be cloned otherwise they will be assigned by reference. If a value has a `clone` method it will be used to perform the clone. Functions, DOM nodes, `arguments` objects, and objects created by constructors other than `Object` are **not** cloned unless they have a custom `clone` method. #### Arguments 1. `value` *(Mixed)*: The value to clone. +2. `deep` *(Boolean)*: A flag to indicate a deep clone. +3. `[guard]` *(Object)*: Internally used to allow this method to work with others like `_.map` without using their callback `index` argument for `deep`. +4. `[stack=[]]` *(Array)*: Internally used to keep track of traversed objects to avoid circular references. +5. `thorough` *(Object)*: Internally used to indicate whether or not to perform a more thorough clone of non-object values. #### Returns *(Mixed)*: Returns the cloned `value`. #### Example -~~~ js +```js +var stooges = [ + { 'name': 'moe', 'age': 40 }, + { 'name': 'larry', 'age': 50 }, + { 'name': 'curly', 'age': 60 } +]; + _.clone({ 'name': 'moe' }); -// => { 'name': 'moe' }; -~~~ +// => { 'name': 'moe' } + +var shallow = _.clone(stooges); +shallow[0] === stooges[0]; +// => true + +var deep = _.clone(stooges, true); +shallow[0] === stooges[0]; +// => false +``` * * * @@ -328,9 +350,9 @@ _.clone({ 'name': 'moe' }); ### `_.compact(array)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1227 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2384 "View in source") [Ⓣ][1] -Produces a new array with all falsey values of `array` removed. The values `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. +Creates a new array with all falsey values of `array` removed. The values `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. #### Arguments 1. `array` *(Array)*: The array to compact. @@ -339,10 +361,10 @@ Produces a new array with all falsey values of `array` removed. The values `fals *(Array)*: Returns a new filtered array. #### Example -~~~ js +```js _.compact([0, 1, false, 2, '', 3]); // => [1, 2, 3] -~~~ +``` * * * @@ -352,7 +374,7 @@ _.compact([0, 1, false, 2, '', 3]); ### `_.compose([func1, func2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2145 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3307 "View in source") [Ⓣ][1] Creates a new function that is the composition of the passed functions, where each function consumes the return value of the function that follows. In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. @@ -363,13 +385,13 @@ Creates a new function that is the composition of the passed functions, where ea *(Function)*: Returns the new composed function. #### Example -~~~ js +```js var greet = function(name) { return 'hi: ' + name; }; var exclaim = function(statement) { return statement + '!'; }; var welcome = _.compose(exclaim, greet); welcome('moe'); // => 'hi: moe!' -~~~ +``` * * * @@ -379,19 +401,19 @@ welcome('moe'); ### `_.contains(collection, target)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L762 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1811 "View in source") [Ⓣ][1] -Checks if a given `target` value is present in a `collection` using strict equality for comparisons, i.e. `===`. +Checks if a given `target` element is present in a `collection` using strict equality for comparisons, i.e. `===`. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. 2. `target` *(Mixed)*: The value to check for. #### Returns -*(Boolean)*: Returns `true` if `target` value is found, else `false`. +*(Boolean)*: Returns `true` if the `target` element is found, else `false`. #### Example -~~~ js +```js _.contains([1, 2, 3], 3); // => true @@ -400,7 +422,39 @@ _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); _.contains('curly', 'ur'); // => true -~~~ +``` + +* * * + + + + + + +### `_.countBy(collection, callback [, thisArg])` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1847 "View in source") [Ⓣ][1] + +Creates an object composed of keys returned from running each element of `collection` through a `callback`. The corresponding value of each key is the number of times the key was returned by `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to count by *(e.g. 'length')*. + +#### Arguments +1. `collection` *(Array|Object|String)*: The collection to iterate over. +2. `callback` *(Function|String)*: The function called per iteration or property name to count by. +3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. + +#### Returns +*(Object)*: Returns the composed aggregate object. + +#### Example +```js +_.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); +// => { '4': 1, '6': 2 } + +_.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); +// => { '4': 1, '6': 2 } + +_.countBy(['one', 'two', 'three'], 'length'); +// => { '3': 2, '5': 1 } +``` * * * @@ -410,7 +464,7 @@ _.contains('curly', 'ur'); ### `_.debounce(func, wait, immediate)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2178 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3340 "View in source") [Ⓣ][1] Creates a new function that will delay the execution of `func` until after `wait` milliseconds have elapsed since the last time it was invoked. Pass `true` for `immediate` to cause debounce to invoke `func` on the leading, instead of the trailing, edge of the `wait` timeout. Subsequent calls to the debounced function will return the result of the last `func` call. @@ -423,10 +477,10 @@ Creates a new function that will delay the execution of `func` until after `wait *(Function)*: Returns the new debounced function. #### Example -~~~ js +```js var lazyLayout = _.debounce(calculateLayout, 300); jQuery(window).on('resize', lazyLayout); -~~~ +``` * * * @@ -435,24 +489,24 @@ jQuery(window).on('resize', lazyLayout); -### `_.defaults(object [, defaults1, defaults2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2462 "View in source") [Ⓣ][1] +### `_.defaults(object [, default1, default2, ...])` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1095 "View in source") [Ⓣ][1] -Assigns missing properties on `object` with default values from the defaults objects. Once a property is set, additional defaults of the same property will be ignored. +Assigns enumerable properties of the default object(s) to the `destination` object for all `destination` properties that resolve to `null`/`undefined`. Once a property is set, additional defaults of the same property will be ignored. #### Arguments -1. `object` *(Object)*: The object to populate. -2. `[defaults1, defaults2, ...]` *(Object)*: The defaults objects to apply to `object`. +1. `object` *(Object)*: The destination object. +2. `[default1, default2, ...]` *(Object)*: The default objects. #### Returns -*(Object)*: Returns `object`. +*(Object)*: Returns the destination object. #### Example -~~~ js +```js var iceCream = { 'flavor': 'chocolate' }; _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } -~~~ +``` * * * @@ -462,9 +516,9 @@ _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); ### `_.defer(func [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2243 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3405 "View in source") [Ⓣ][1] -Defers executing the `func` function until the current call stack has cleared. Additional arguments are passed to `func` when it is invoked. +Defers executing the `func` function until the current call stack has cleared. Additional arguments will be passed to `func` when it is invoked. #### Arguments 1. `func` *(Function)*: The function to defer. @@ -474,10 +528,10 @@ Defers executing the `func` function until the current call stack has cleared. A *(Number)*: Returns the `setTimeout` timeout id. #### Example -~~~ js +```js _.defer(function() { alert('deferred'); }); // returns from the function before `alert` is called -~~~ +``` * * * @@ -487,9 +541,9 @@ _.defer(function() { alert('deferred'); }); ### `_.delay(func, wait [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2223 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3385 "View in source") [Ⓣ][1] -Executes the `func` function after `wait` milliseconds. Additional arguments are passed to `func` when it is invoked. +Executes the `func` function after `wait` milliseconds. Additional arguments will be passed to `func` when it is invoked. #### Arguments 1. `func` *(Function)*: The function to delay. @@ -500,11 +554,11 @@ Executes the `func` function after `wait` milliseconds. Additional arguments are *(Number)*: Returns the `setTimeout` timeout id. #### Example -~~~ js +```js var log = _.bind(console.log, console); _.delay(log, 1000, 'logged later'); // => 'logged later' (Appears after one second.) -~~~ +``` * * * @@ -514,22 +568,47 @@ _.delay(log, 1000, 'logged later'); ### `_.difference(array [, array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1259 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2416 "View in source") [Ⓣ][1] -Produces a new array of `array` values not present in the other arrays using strict equality for comparisons, i.e. `===`. +Creates a new array of `array` elements not present in the other arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `array` *(Array)*: The array to process. 2. `[array1, array2, ...]` *(Array)*: Arrays to check. #### Returns -*(Array)*: Returns a new array of `array` values not present in the other arrays. +*(Array)*: Returns a new array of `array` elements not present in the other arrays. #### Example -~~~ js +```js _.difference([1, 2, 3, 4, 5], [5, 2, 10]); // => [1, 3, 4] -~~~ +``` + +* * * + + + + + + +### `_.drop(object [, prop1, prop2, ...])` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1115 "View in source") [Ⓣ][1] + +Creates a shallow clone of `object` excluding the specified properties. Property names may be specified as individual arguments or as arrays of property names. + +#### Arguments +1. `object` *(Object)*: The source object. +2. `[prop1, prop2, ...]` *(Object)*: The properties to drop. + +#### Returns +*(Object)*: Returns an object without the dropped properties. + +#### Example +```js +_.drop({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); +// => { 'name': 'moe', 'age': 40 } +``` * * * @@ -539,7 +618,7 @@ _.difference([1, 2, 3, 4, 5], [5, 2, 10]); ### `_.escape(string)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3173 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3602 "View in source") [Ⓣ][1] Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` characters. @@ -550,10 +629,10 @@ Escapes a string for inclusion in HTML, replacing `&`, `<`, `"`, and `'` charact *(String)*: Returns the escaped string. #### Example -~~~ js -_.escape('Curly, Larry & Moe'); -// => "Curly, Larry & Moe" -~~~ +```js +_.escape('Moe, Larry & Curly'); +// => "Moe, Larry & Curly" +``` * * * @@ -563,7 +642,7 @@ _.escape('Curly, Larry & Moe'); ### `_.every(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L790 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1867 "View in source") [Ⓣ][1] Checks if the `callback` returns a truthy value for **all** elements of a `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. @@ -573,13 +652,13 @@ Checks if the `callback` returns a truthy value for **all** elements of a `colle 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Boolean)*: Returns `true` if all values pass the callback check, else `false`. +*(Boolean)*: Returns `true` if all elements pass the callback check, else `false`. #### Example -~~~ js +```js _.every([true, 1, null, 'yes'], Boolean); // => false -~~~ +``` * * * @@ -589,9 +668,9 @@ _.every([true, 1, null, 'yes'], Boolean); ### `_.extend(object [, source1, source2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2481 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1139 "View in source") [Ⓣ][1] -Copies enumerable properties from the source objects to the `destination` object. Subsequent sources will overwrite propery assignments of previous sources. +Assigns enumerable properties of the source object(s) to the `destination` object. Subsequent sources will overwrite propery assignments of previous sources. #### Arguments 1. `object` *(Object)*: The destination object. @@ -601,10 +680,10 @@ Copies enumerable properties from the source objects to the `destination` object *(Object)*: Returns the destination object. #### Example -~~~ js +```js _.extend({ 'name': 'moe' }, { 'age': 40 }); // => { 'name': 'moe', 'age': 40 } -~~~ +``` * * * @@ -614,9 +693,9 @@ _.extend({ 'name': 'moe' }, { 'age': 40 }); ### `_.filter(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L810 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1887 "View in source") [Ⓣ][1] -Examines each value in a `collection`, returning an array of all values the `callback` returns truthy for. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. +Examines each element in a `collection`, returning an array of all elements the `callback` returns truthy for. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -624,13 +703,13 @@ Examines each value in a `collection`, returning an array of all values the `cal 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Array)*: Returns a new array of values that passed callback check. +*(Array)*: Returns a new array of elements that passed callback check. #### Example -~~~ js +```js var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // => [2, 4, 6] -~~~ +``` * * * @@ -640,9 +719,9 @@ var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }) ### `_.find(collection, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L831 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1908 "View in source") [Ⓣ][1] -Examines each value in a `collection`, returning the first one the `callback` returns truthy for. The function returns as soon as it finds an acceptable value, and does not iterate over the entire `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. +Examines each element in a `collection`, returning the first one the `callback` returns truthy for. The function returns as soon as it finds an acceptable element, and does not iterate over the entire `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -650,13 +729,13 @@ Examines each value in a `collection`, returning the first one the `callback` re 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Mixed)*: Returns the value that passed the callback check, else `undefined`. +*(Mixed)*: Returns the element that passed the callback check, else `undefined`. #### Example -~~~ js +```js var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // => 2 -~~~ +``` * * * @@ -666,9 +745,9 @@ var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); ### `_.first(array [, n, guard])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1296 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2453 "View in source") [Ⓣ][1] -Gets the first value of the `array`. Pass `n` to return the first `n` values of the `array`. +Gets the first element of the `array`. Pass `n` to return the first `n` elements of the `array`. #### Arguments 1. `array` *(Array)*: The array to query. @@ -676,13 +755,13 @@ Gets the first value of the `array`. Pass `n` to return the first `n` values of 3. `[guard]` *(Object)*: Internally used to allow this method to work with others like `_.map` without using their callback `index` argument for `n`. #### Returns -*(Mixed)*: Returns the first value or an array of the first `n` values of `array`. +*(Mixed)*: Returns the first element or an array of the first `n` elements of `array`. #### Example -~~~ js +```js _.first([5, 4, 3, 2, 1]); // => 5 -~~~ +``` * * * @@ -692,7 +771,7 @@ _.first([5, 4, 3, 2, 1]); ### `_.flatten(array, shallow)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1320 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2477 "View in source") [Ⓣ][1] Flattens a nested array *(the nesting can be to any depth)*. If `shallow` is truthy, `array` will only be flattened a single level. @@ -704,13 +783,13 @@ Flattens a nested array *(the nesting can be to any depth)*. If `shallow` is tru *(Array)*: Returns a new flattened array. #### Example -~~~ js +```js _.flatten([1, [2], [3, [[4]]]]); // => [1, 2, 3, 4]; _.flatten([1, [2], [3, [[4]]]], true); // => [1, 2, 3, [[4]]]; -~~~ +``` * * * @@ -720,9 +799,9 @@ _.flatten([1, [2], [3, [[4]]]], true); ### `_.forEach(collection, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L857 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1935 "View in source") [Ⓣ][1] -Iterates over a `collection`, executing the `callback` for each value in the `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. +Iterates over a `collection`, executing the `callback` for each element in the `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. Callbacks may exit iteration early by explicitly returning `false`. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -730,16 +809,16 @@ Iterates over a `collection`, executing the `callback` for each value in the `co 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Array, Object)*: Returns the `collection`. +*(Array, Object)*: Returns `collection`. #### Example -~~~ js +```js _([1, 2, 3]).forEach(alert).join(','); // => alerts each number and returns '1,2,3' _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); // => alerts each number (order is not guaranteed) -~~~ +``` * * * @@ -749,9 +828,9 @@ _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); ### `_.forIn(object, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2510 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1169 "View in source") [Ⓣ][1] -Iterates over `object`'s own and inherited enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, key, object)*. +Iterates over `object`'s own and inherited enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, key, object)*. Callbacks may exit iteration early by explicitly returning `false`. #### Arguments 1. `object` *(Object)*: The object to iterate over. @@ -759,10 +838,10 @@ Iterates over `object`'s own and inherited enumerable properties, executing the 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Object)*: Returns the `object`. +*(Object)*: Returns `object`. #### Example -~~~ js +```js function Dog(name) { this.name = name; } @@ -775,7 +854,7 @@ _.forIn(new Dog('Dagny'), function(value, key) { alert(key); }); // => alerts 'name' and 'bark' (order is not guaranteed) -~~~ +``` * * * @@ -785,9 +864,9 @@ _.forIn(new Dog('Dagny'), function(value, key) { ### `_.forOwn(object, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2533 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1193 "View in source") [Ⓣ][1] -Iterates over `object`'s own enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, key, object)*. +Iterates over `object`'s own enumerable properties, executing the `callback` for each property. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, key, object)*. Callbacks may exit iteration early by explicitly returning `false`. #### Arguments 1. `object` *(Object)*: The object to iterate over. @@ -795,15 +874,15 @@ Iterates over `object`'s own enumerable properties, executing the `callback` for 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Object)*: Returns the `object`. +*(Object)*: Returns `object`. #### Example -~~~ js +```js _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { alert(key); }); // => alerts '0', '1', and 'length' (order is not guaranteed) -~~~ +``` * * * @@ -813,9 +892,9 @@ _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { ### `_.functions(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2550 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1210 "View in source") [Ⓣ][1] -Produces a sorted array of the enumerable properties, own and inherited, of `object` that have function values. +Creates a sorted array of all enumerable properties, own and inherited, of `object` that have function values. #### Arguments 1. `object` *(Object)*: The object to inspect. @@ -824,10 +903,10 @@ Produces a sorted array of the enumerable properties, own and inherited, of `obj *(Array)*: Returns a new array of property names that have function values. #### Example -~~~ js +```js _.functions(_); // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] -~~~ +``` * * * @@ -837,9 +916,9 @@ _.functions(_); ### `_.groupBy(collection, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L884 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1963 "View in source") [Ⓣ][1] -Splits `collection` into sets, grouped by the result of running each value through `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to group by. +Creates an object composed of keys returned from running each element of `collection` through a `callback`. The corresponding value of each key is an array of elements passed to `callback` that returned the key. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to count by *(e.g. 'length')*. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -847,19 +926,19 @@ Splits `collection` into sets, grouped by the result of running each value throu 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Object)*: Returns an object of grouped values. +*(Object)*: Returns the composed aggregate object. #### Example -~~~ js -_.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); -// => { '1': [1.3], '2': [2.1, 2.4] } +```js +_.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); +// => { '4': [4.2], '6': [6.1, 6.4] } -_.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); -// => { '1': [1.3], '2': [2.1, 2.4] } +_.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); +// => { '4': [4.2], '6': [6.1, 6.4] } _.groupBy(['one', 'two', 'three'], 'length'); // => { '3': ['one', 'two'], '5': ['three'] } -~~~ +``` * * * @@ -869,7 +948,7 @@ _.groupBy(['one', 'two', 'three'], 'length'); ### `_.has(object, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2573 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1233 "View in source") [Ⓣ][1] Checks if the specified object `property` exists and is a direct property, instead of an inherited property. @@ -881,10 +960,10 @@ Checks if the specified object `property` exists and is a direct property, inste *(Boolean)*: Returns `true` if key is a direct property, else `false`. #### Example -~~~ js +```js _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); // => true -~~~ +``` * * * @@ -894,9 +973,9 @@ _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); ### `_.identity(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3192 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3622 "View in source") [Ⓣ][1] -This function returns the first argument passed to it. Note: It is used throughout Lo-Dash as a default callback. +This function returns the first argument passed to it. Note: It is used throughout Lo-Dash as a default callback. #### Arguments 1. `value` *(Mixed)*: Any value. @@ -905,11 +984,11 @@ This function returns the first argument passed to it. Note: It is used througho *(Mixed)*: Returns `value`. #### Example -~~~ js +```js var moe = { 'name': 'moe' }; moe === _.identity(moe); // => true -~~~ +``` * * * @@ -919,7 +998,7 @@ moe === _.identity(moe); ### `_.indexOf(array, value [, fromIndex=0])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1364 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2521 "View in source") [Ⓣ][1] Gets the index at which the first occurrence of `value` is found using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `isSorted` will run a faster binary search. @@ -932,7 +1011,7 @@ Gets the index at which the first occurrence of `value` is found using strict eq *(Number)*: Returns the index of the matched value or `-1`. #### Example -~~~ js +```js _.indexOf([1, 2, 3, 1, 2, 3], 2); // => 1 @@ -941,7 +1020,7 @@ _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); _.indexOf([1, 1, 2, 2, 3, 3], 2, true); // => 2 -~~~ +``` * * * @@ -951,9 +1030,9 @@ _.indexOf([1, 1, 2, 2, 3, 3], 2, true); ### `_.initial(array [, n, guard])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1404 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2561 "View in source") [Ⓣ][1] -Gets all but the last value of `array`. Pass `n` to exclude the last `n` values from the result. +Gets all but the last element of `array`. Pass `n` to exclude the last `n` elements from the result. #### Arguments 1. `array` *(Array)*: The array to query. @@ -961,13 +1040,13 @@ Gets all but the last value of `array`. Pass `n` to exclude the last `n` values 3. `[guard]` *(Object)*: Internally used to allow this method to work with others like `_.map` without using their callback `index` argument for `n`. #### Returns -*(Array)*: Returns all but the last value or `n` values of `array`. +*(Array)*: Returns all but the last element or `n` elements of `array`. #### Example -~~~ js +```js _.initial([3, 2, 1]); // => [3, 2] -~~~ +``` * * * @@ -977,21 +1056,21 @@ _.initial([3, 2, 1]); ### `_.intersection([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1425 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2583 "View in source") [Ⓣ][1] -Computes the intersection of all the passed-in arrays. +Computes the intersection of all the passed-in arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `[array1, array2, ...]` *(Array)*: Arrays to process. #### Returns -*(Array)*: Returns a new array of unique values, in order, that are present in **all** of the arrays. +*(Array)*: Returns a new array of unique elements, in order, that are present in **all** of the arrays. #### Example -~~~ js +```js _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); // => [1, 2] -~~~ +``` * * * @@ -1001,7 +1080,7 @@ _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); ### `_.invoke(collection, methodName [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L918 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1991 "View in source") [Ⓣ][1] Invokes the method named by `methodName` on each element in the `collection`. Additional arguments will be passed to each invoked method. If `methodName` is a function it will be invoked for, and `this` bound to, each element in the `collection`. @@ -1014,13 +1093,13 @@ Invokes the method named by `methodName` on each element in the `collection`. Ad *(Array)*: Returns a new array of values returned from each invoked method. #### Example -~~~ js +```js _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); // => [[1, 5, 7], [1, 2, 3]] _.invoke([123, 456], String.prototype.split, ''); // => [['1', '2', '3'], ['4', '5', '6']] -~~~ +``` * * * @@ -1030,7 +1109,7 @@ _.invoke([123, 456], String.prototype.split, ''); ### `_.isArguments(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2593 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L886 "View in source") [Ⓣ][1] Checks if `value` is an `arguments` object. @@ -1041,13 +1120,13 @@ Checks if `value` is an `arguments` object. *(Boolean)*: Returns `true` if the `value` is an `arguments` object, else `false`. #### Example -~~~ js +```js (function() { return _.isArguments(arguments); })(1, 2, 3); // => true _.isArguments([1, 2, 3]); // => false -~~~ +``` * * * @@ -1057,7 +1136,7 @@ _.isArguments([1, 2, 3]); ### `_.isArray(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2620 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L912 "View in source") [Ⓣ][1] Checks if `value` is an array. @@ -1068,13 +1147,13 @@ Checks if `value` is an array. *(Boolean)*: Returns `true` if the `value` is an array, else `false`. #### Example -~~~ js +```js (function() { return _.isArray(arguments); })(); // => false _.isArray([1, 2, 3]); // => true -~~~ +``` * * * @@ -1084,7 +1163,7 @@ _.isArray([1, 2, 3]); ### `_.isBoolean(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2637 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1250 "View in source") [Ⓣ][1] Checks if `value` is a boolean *(`true` or `false`)* value. @@ -1095,10 +1174,10 @@ Checks if `value` is a boolean *(`true` or `false`)* value. *(Boolean)*: Returns `true` if the `value` is a boolean value, else `false`. #### Example -~~~ js +```js _.isBoolean(null); // => false -~~~ +``` * * * @@ -1108,7 +1187,7 @@ _.isBoolean(null); ### `_.isDate(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2654 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1267 "View in source") [Ⓣ][1] Checks if `value` is a date. @@ -1119,10 +1198,10 @@ Checks if `value` is a date. *(Boolean)*: Returns `true` if the `value` is a date, else `false`. #### Example -~~~ js +```js _.isDate(new Date); // => true -~~~ +``` * * * @@ -1132,7 +1211,7 @@ _.isDate(new Date); ### `_.isElement(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2671 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1284 "View in source") [Ⓣ][1] Checks if `value` is a DOM element. @@ -1143,10 +1222,10 @@ Checks if `value` is a DOM element. *(Boolean)*: Returns `true` if the `value` is a DOM element, else `false`. #### Example -~~~ js +```js _.isElement(document.body); // => true -~~~ +``` * * * @@ -1156,9 +1235,9 @@ _.isElement(document.body); ### `_.isEmpty(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2695 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1309 "View in source") [Ⓣ][1] -Checks if `value` is empty. Arrays or strings with a length of `0` and objects with no own enumerable properties are considered "empty". +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". #### Arguments 1. `value` *(Array|Object|String)*: The value to inspect. @@ -1167,7 +1246,7 @@ Checks if `value` is empty. Arrays or strings with a length of `0` and objects w *(Boolean)*: Returns `true` if the `value` is empty, else `false`. #### Example -~~~ js +```js _.isEmpty([1, 2, 3]); // => false @@ -1176,7 +1255,7 @@ _.isEmpty({}); _.isEmpty(''); // => true -~~~ +``` * * * @@ -1185,21 +1264,22 @@ _.isEmpty(''); -### `_.isEqual(a, b [, stack])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2729 "View in source") [Ⓣ][1] +### `_.isEqual(a, b [, stack=[]], thorough)` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1351 "View in source") [Ⓣ][1] -Performs a deep comparison between two values to determine if they are equivalent to each other. +Performs a deep comparison between two values to determine if they are equivalent to each other. If a value has an `isEqual` method it will be used to perform the comparison. #### Arguments 1. `a` *(Mixed)*: The value to compare. 2. `b` *(Mixed)*: The other value to compare. -3. `[stack]` *(Array)*: Internally used to keep track of "seen" objects to avoid circular references. +3. `[stack=[]]` *(Array)*: Internally used to keep track of traversed objects to avoid circular references. +4. `thorough` *(Object)*: Internally used to indicate whether or not to perform a more thorough comparison of non-object values. #### Returns *(Boolean)*: Returns `true` if the values are equvalent, else `false`. #### Example -~~~ js +```js var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; @@ -1208,7 +1288,7 @@ moe == clone; _.isEqual(moe, clone); // => true -~~~ +``` * * * @@ -1218,9 +1298,9 @@ _.isEqual(moe, clone); ### `_.isFinite(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2891 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1524 "View in source") [Ⓣ][1] -Checks if `value` is a finite number. Note: This is not the same as native `isFinite`, which will return true for booleans and other values. See http://es5.github.com/#x15.1.2.5. +Checks if `value` is a finite number. Note: This is not the same as native `isFinite`, which will return true for booleans and other values. See http://es5.github.com/#x15.1.2.5. #### Arguments 1. `value` *(Mixed)*: The value to check. @@ -1229,7 +1309,7 @@ Checks if `value` is a finite number. Note: This is not the same as native `isFi *(Boolean)*: Returns `true` if the `value` is a finite number, else `false`. #### Example -~~~ js +```js _.isFinite(-101); // => true @@ -1238,7 +1318,7 @@ _.isFinite('10'); _.isFinite(Infinity); // => false -~~~ +``` * * * @@ -1248,7 +1328,7 @@ _.isFinite(Infinity); ### `_.isFunction(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2908 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L929 "View in source") [Ⓣ][1] Checks if `value` is a function. @@ -1259,10 +1339,10 @@ Checks if `value` is a function. *(Boolean)*: Returns `true` if the `value` is a function, else `false`. #### Example -~~~ js +```js _.isFunction(''.concat); // => true -~~~ +``` * * * @@ -1272,9 +1352,9 @@ _.isFunction(''.concat); ### `_.isNaN(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2960 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1579 "View in source") [Ⓣ][1] -Checks if `value` is `NaN`. Note: This is not the same as native `isNaN`, which will return true for `undefined` and other values. See http://es5.github.com/#x15.1.2.4. +Checks if `value` is `NaN`. Note: This is not the same as native `isNaN`, which will return true for `undefined` and other values. See http://es5.github.com/#x15.1.2.4. #### Arguments 1. `value` *(Mixed)*: The value to check. @@ -1283,7 +1363,7 @@ Checks if `value` is `NaN`. Note: This is not the same as native `isNaN`, which *(Boolean)*: Returns `true` if the `value` is `NaN`, else `false`. #### Example -~~~ js +```js _.isNaN(NaN); // => true @@ -1295,7 +1375,7 @@ isNaN(undefined); _.isNaN(undefined); // => false -~~~ +``` * * * @@ -1305,7 +1385,7 @@ _.isNaN(undefined); ### `_.isNull(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2983 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1602 "View in source") [Ⓣ][1] Checks if `value` is `null`. @@ -1316,13 +1396,13 @@ Checks if `value` is `null`. *(Boolean)*: Returns `true` if the `value` is `null`, else `false`. #### Example -~~~ js +```js _.isNull(null); // => true _.isNull(undefined); // => false -~~~ +``` * * * @@ -1332,7 +1412,7 @@ _.isNull(undefined); ### `_.isNumber(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3000 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1619 "View in source") [Ⓣ][1] Checks if `value` is a number. @@ -1343,10 +1423,10 @@ Checks if `value` is a number. *(Boolean)*: Returns `true` if the `value` is a number, else `false`. #### Example -~~~ js +```js _.isNumber(8.4 * 5; // => true -~~~ +``` * * * @@ -1356,9 +1436,9 @@ _.isNumber(8.4 * 5; ### `_.isObject(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2929 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1545 "View in source") [Ⓣ][1] -Checks if `value` is the language type of Object. *(e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`)* +Checks if `value` is the language type of Object. *(e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)* #### Arguments 1. `value` *(Mixed)*: The value to check. @@ -1367,13 +1447,13 @@ Checks if `value` is the language type of Object. *(e.g. arrays, functions, obje *(Boolean)*: Returns `true` if the `value` is an object, else `false`. #### Example -~~~ js +```js _.isObject({}); // => true _.isObject(1); // => false -~~~ +``` * * * @@ -1383,7 +1463,7 @@ _.isObject(1); ### `_.isRegExp(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3017 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1636 "View in source") [Ⓣ][1] Checks if `value` is a regular expression. @@ -1394,10 +1474,10 @@ Checks if `value` is a regular expression. *(Boolean)*: Returns `true` if the `value` is a regular expression, else `false`. #### Example -~~~ js +```js _.isRegExp(/moe/); // => true -~~~ +``` * * * @@ -1407,7 +1487,7 @@ _.isRegExp(/moe/); ### `_.isString(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3034 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1653 "View in source") [Ⓣ][1] Checks if `value` is a string. @@ -1418,10 +1498,10 @@ Checks if `value` is a string. *(Boolean)*: Returns `true` if the `value` is a string, else `false`. #### Example -~~~ js +```js _.isString('moe'); // => true -~~~ +``` * * * @@ -1431,7 +1511,7 @@ _.isString('moe'); ### `_.isUndefined(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3052 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1671 "View in source") [Ⓣ][1] Checks if `value` is `undefined`. @@ -1442,10 +1522,10 @@ Checks if `value` is `undefined`. *(Boolean)*: Returns `true` if the `value` is `undefined`, else `false`. #### Example -~~~ js +```js _.isUndefined(void 0); // => true -~~~ +``` * * * @@ -1455,9 +1535,9 @@ _.isUndefined(void 0); ### `_.keys(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3069 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1688 "View in source") [Ⓣ][1] -Produces an array of object`'s own enumerable property names. +Creates an array composed of the own enumerable property names of `object`. #### Arguments 1. `object` *(Object)*: The object to inspect. @@ -1466,10 +1546,10 @@ Produces an array of object`'s own enumerable property names. *(Array)*: Returns a new array of property names. #### Example -~~~ js +```js _.keys({ 'one': 1, 'two': 2, 'three': 3 }); // => ['one', 'two', 'three'] (order is not guaranteed) -~~~ +``` * * * @@ -1479,9 +1559,9 @@ _.keys({ 'one': 1, 'two': 2, 'three': 3 }); ### `_.last(array [, n, guard])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1466 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2624 "View in source") [Ⓣ][1] -Gets the last value of the `array`. Pass `n` to return the lasy `n` values of the `array`. +Gets the last element of the `array`. Pass `n` to return the lasy `n` elementsvof the `array`. #### Arguments 1. `array` *(Array)*: The array to query. @@ -1489,13 +1569,13 @@ Gets the last value of the `array`. Pass `n` to return the lasy `n` values of th 3. `[guard]` *(Object)*: Internally used to allow this method to work with others like `_.map` without using their callback `index` argument for `n`. #### Returns -*(Mixed)*: Returns the last value or an array of the last `n` values of `array`. +*(Mixed)*: Returns the last element or an array of the last `n` elements of `array`. #### Example -~~~ js +```js _.last([3, 2, 1]); // => 1 -~~~ +``` * * * @@ -1505,7 +1585,7 @@ _.last([3, 2, 1]); ### `_.lastIndexOf(array, value [, fromIndex=array.length-1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1492 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2650 "View in source") [Ⓣ][1] Gets the index at which the last occurrence of `value` is found using strict equality for comparisons, i.e. `===`. @@ -1518,13 +1598,13 @@ Gets the index at which the last occurrence of `value` is found using strict equ *(Number)*: Returns the index of the matched value or `-1`. #### Example -~~~ js +```js _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); // => 4 _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); // => 1 -~~~ +``` * * * @@ -1534,9 +1614,9 @@ _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); ### `_.map(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L954 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2026 "View in source") [Ⓣ][1] -Produces a new array of values by mapping each element in the `collection` through a transformation `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. +Creates a new array of values by running each element in the `collection` through a `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -1544,16 +1624,16 @@ Produces a new array of values by mapping each element in the `collection` throu 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Array)*: Returns a new array of values returned by the callback. +*(Array)*: Returns a new array of elements returned by the callback. #### Example -~~~ js +```js _.map([1, 2, 3], function(num) { return num * 3; }); // => [3, 6, 9] _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); // => [3, 6, 9] (order is not guaranteed) -~~~ +``` * * * @@ -1563,7 +1643,7 @@ _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); ### `_.max(array [, callback, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1532 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2690 "View in source") [Ⓣ][1] Retrieves the maximum value of an `array`. If `callback` is passed, it will be executed for each value in the `array` to generate the criterion by which the value is ranked. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index, array)*. @@ -1576,7 +1656,7 @@ Retrieves the maximum value of an `array`. If `callback` is passed, it will be e *(Mixed)*: Returns the maximum value. #### Example -~~~ js +```js var stooges = [ { 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }, @@ -1585,7 +1665,7 @@ var stooges = [ _.max(stooges, function(stooge) { return stooge.age; }); // => { 'name': 'curly', 'age': 60 }; -~~~ +``` * * * @@ -1595,7 +1675,7 @@ _.max(stooges, function(stooge) { return stooge.age; }); ### `_.memoize(func [, resolver])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2266 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3428 "View in source") [Ⓣ][1] Creates a new function that memoizes the result of `func`. If `resolver` is passed, it will be used to determine the cache key for storing the result based on the arguments passed to the memoized function. By default, the first argument passed to the memoized function is used as the cache key. @@ -1607,11 +1687,48 @@ Creates a new function that memoizes the result of `func`. If `resolver` is pass *(Function)*: Returns the new memoizing function. #### Example -~~~ js +```js var fibonacci = _.memoize(function(n) { return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); }); -~~~ +``` + +* * * + + + + + + +### `_.merge(object [, source1, source2, ..., indicator, stack=[]])` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2058 "View in source") [Ⓣ][1] + +Merges enumerable properties of the source object(s) into the `destination` object. Subsequent sources will overwrite propery assignments of previous sources. + +#### Arguments +1. `object` *(Object)*: The destination object. +2. `[source1, source2, ...]` *(Object)*: The source objects. +3. `[indicator]` *(Object)*: Internally used to indicate that the `stack` argument is an array of traversed objects instead of another source object. +4. `[stack=[]]` *(Array)*: Internally used to keep track of traversed objects to avoid circular references. + +#### Returns +*(Object)*: Returns the destination object. + +#### Example +```js +var stooges = [ + { 'name': 'moe' }, + { 'name': 'larry' } +]; + +var ages = [ + { 'age': 40 }, + { 'age': 50 } +]; + +_.merge(stooges, ages); +// => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] +``` * * * @@ -1621,7 +1738,7 @@ var fibonacci = _.memoize(function(n) { ### `_.min(array [, callback, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1582 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2740 "View in source") [Ⓣ][1] Retrieves the minimum value of an `array`. If `callback` is passed, it will be executed for each value in the `array` to generate the criterion by which the value is ranked. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index, array)*. @@ -1634,10 +1751,10 @@ Retrieves the minimum value of an `array`. If `callback` is passed, it will be e *(Mixed)*: Returns the minimum value. #### Example -~~~ js +```js _.min([10, 5, 100, 2, 1000]); // => 2 -~~~ +``` * * * @@ -1647,7 +1764,7 @@ _.min([10, 5, 100, 2, 1000]); ### `_.mixin(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3218 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3648 "View in source") [Ⓣ][1] Adds functions properties of `object` to the `lodash` function and chainable wrapper. @@ -1655,19 +1772,19 @@ Adds functions properties of `object` to the `lodash` function and chainable wra 1. `object` *(Object)*: The object of function properties to add to `lodash`. #### Example -~~~ js +```js _.mixin({ 'capitalize': function(string) { return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); } }); -_.capitalize('curly'); -// => 'Curly' - -_('larry').capitalize(); +_.capitalize('larry'); // => 'Larry' -~~~ + +_('curly').capitalize(); +// => 'Curly' +``` * * * @@ -1677,7 +1794,7 @@ _('larry').capitalize(); ### `_.noConflict()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3249 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3679 "View in source") [Ⓣ][1] Reverts the '_' variable to its previous value and returns a reference to the `lodash` function. @@ -1685,9 +1802,9 @@ Reverts the '_' variable to its previous value and returns a reference to the `l *(Function)*: Returns the `lodash` function. #### Example -~~~ js +```js var lodash = _.noConflict(); -~~~ +``` * * * @@ -1697,7 +1814,7 @@ var lodash = _.noConflict(); ### `_.once(func)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2292 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3454 "View in source") [Ⓣ][1] Creates a new function that is restricted to one execution. Repeat calls to the function will return the value of the first call. @@ -1708,12 +1825,12 @@ Creates a new function that is restricted to one execution. Repeat calls to the *(Function)*: Returns the new restricted function. #### Example -~~~ js +```js var initialize = _.once(createApplication); initialize(); initialize(); // Application is only created once. -~~~ +``` * * * @@ -1723,9 +1840,9 @@ initialize(); ### `_.partial(func [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2325 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3489 "View in source") [Ⓣ][1] -Creates a new function that, when called, invokes `func` with any additional `partial` arguments prepended to those passed to the partially applied function. This method is similar `bind`, except it does **not** alter the `this` binding. +Creates a new function that, when called, invokes `func` with any additional `partial` arguments prepended to those passed to the new function. This method is similar `bind`, except it does **not** alter the `this` binding. #### Arguments 1. `func` *(Function)*: The function to partially apply arguments to. @@ -1735,12 +1852,12 @@ Creates a new function that, when called, invokes `func` with any additional `pa *(Function)*: Returns the new partially applied function. #### Example -~~~ js +```js var greet = function(greeting, name) { return greeting + ': ' + name; }; var hi = _.partial(greet, 'hi'); hi('moe'); // => 'hi: moe' -~~~ +``` * * * @@ -1750,22 +1867,22 @@ hi('moe'); ### `_.pick(object [, prop1, prop2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3091 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1711 "View in source") [Ⓣ][1] -Creates an object composed of the specified properties. Property names may be specified as individual arguments or as arrays of property names. +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. #### Arguments -1. `object` *(Object)*: The object to pluck. +1. `object` *(Object)*: The source object. 2. `[prop1, prop2, ...]` *(Object)*: The properties to pick. #### Returns *(Object)*: Returns an object composed of the picked properties. #### Example -~~~ js +```js _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); // => { 'name': 'moe', 'age': 40 } -~~~ +``` * * * @@ -1775,7 +1892,7 @@ _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); ### `_.pluck(collection, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L977 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2106 "View in source") [Ⓣ][1] Retrieves the value of a specified property from all elements in the `collection`. @@ -1787,7 +1904,7 @@ Retrieves the value of a specified property from all elements in the `collection *(Array)*: Returns a new array of property values. #### Example -~~~ js +```js var stooges = [ { 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }, @@ -1796,7 +1913,7 @@ var stooges = [ _.pluck(stooges, 'name'); // => ['moe', 'larry', 'curly'] -~~~ +``` * * * @@ -1806,7 +1923,7 @@ _.pluck(stooges, 'name'); ### `_.range([start=0], end [, step=1])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1643 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2801 "View in source") [Ⓣ][1] Creates an array of numbers *(positive and/or negative)* progressing from `start` up to but not including `stop`. This method is a port of Python's `range()` function. See http://docs.python.org/library/functions.html#range. @@ -1819,7 +1936,7 @@ Creates an array of numbers *(positive and/or negative)* progressing from `start *(Array)*: Returns a new range array. #### Example -~~~ js +```js _.range(10); // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] @@ -1834,7 +1951,7 @@ _.range(0, -10, -1); _.range(0); // => [] -~~~ +``` * * * @@ -1844,7 +1961,7 @@ _.range(0); ### `_.reduce(collection, callback [, accumulator, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1005 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2134 "View in source") [Ⓣ][1] Boils down a `collection` to a single value. The initial state of the reduction is `accumulator` and each successive step of it should be returned by the `callback`. The `callback` is bound to `thisArg` and invoked with `4` arguments; for arrays they are *(accumulator, value, index|key, collection)*. @@ -1858,10 +1975,10 @@ Boils down a `collection` to a single value. The initial state of the reduction *(Mixed)*: Returns the accumulated value. #### Example -~~~ js +```js var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); // => 6 -~~~ +``` * * * @@ -1871,7 +1988,7 @@ var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); ### `_.reduceRight(collection, callback [, accumulator, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1042 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2171 "View in source") [Ⓣ][1] The right-associative version of `_.reduce`. @@ -1885,11 +2002,11 @@ The right-associative version of `_.reduce`. *(Mixed)*: Returns the accumulated value. #### Example -~~~ js +```js var list = [[0, 1], [2, 3], [4, 5]]; var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); // => [4, 5, 2, 3, 0, 1] -~~~ +``` * * * @@ -1899,7 +2016,7 @@ var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); ### `_.reject(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1097 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2227 "View in source") [Ⓣ][1] The opposite of `_.filter`, this method returns the values of a `collection` that `callback` does **not** return truthy for. @@ -1909,13 +2026,13 @@ The opposite of `_.filter`, this method returns the values of a `collection` tha 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Array)*: Returns a new array of values that did **not** pass the callback check. +*(Array)*: Returns a new array of elements that did **not** pass the callback check. #### Example -~~~ js +```js var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); // => [1, 3, 5] -~~~ +``` * * * @@ -1925,7 +2042,7 @@ var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); ### `_.rest(array [, n, guard])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1680 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2840 "View in source") [Ⓣ][1] The opposite of `_.initial`, this method gets all but the first value of `array`. Pass `n` to exclude the first `n` values from the result. @@ -1938,10 +2055,10 @@ The opposite of `_.initial`, this method gets all but the first value of `array` *(Array)*: Returns all but the first value or `n` values of `array`. #### Example -~~~ js +```js _.rest([3, 2, 1]); // => [2, 1] -~~~ +``` * * * @@ -1951,7 +2068,7 @@ _.rest([3, 2, 1]); ### `_.result(object, property)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3281 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3711 "View in source") [Ⓣ][1] Resolves the value of `property` on `object`. If `property` is a function it will be invoked and its result returned, else the property value is returned. If `object` is falsey, then `null` is returned. @@ -1963,7 +2080,7 @@ Resolves the value of `property` on `object`. If `property` is a function it wil *(Mixed)*: Returns the resolved value. #### Example -~~~ js +```js var object = { 'cheese': 'crumpets', 'stuff': function() { @@ -1976,7 +2093,7 @@ _.result(object, 'cheese'); _.result(object, 'stuff'); // => 'nonsense' -~~~ +``` * * * @@ -1986,9 +2103,9 @@ _.result(object, 'stuff'); ### `_.shuffle(array)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1701 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2861 "View in source") [Ⓣ][1] -Produces a new array of shuffled `array` values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. +Creates a new array of shuffled `array` values, using a version of the Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. #### Arguments 1. `array` *(Array)*: The array to shuffle. @@ -1997,10 +2114,10 @@ Produces a new array of shuffled `array` values, using a version of the Fisher-Y *(Array)*: Returns a new shuffled array. #### Example -~~~ js +```js _.shuffle([1, 2, 3, 4, 5, 6]); // => [4, 1, 6, 3, 5, 2] -~~~ +``` * * * @@ -2010,18 +2127,18 @@ _.shuffle([1, 2, 3, 4, 5, 6]); ### `_.size(value)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3130 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1750 "View in source") [Ⓣ][1] -Gets the size of `value` by returning `value.length` if `value` is a string or array, or the number of own enumerable properties if `value` is an object. +Gets the size of `value` by returning `value.length` if `value` is an array, string, or `arguments` object. If `value` is an object, size is determined by returning the number of own enumerable properties it has. #### Arguments 1. `value` *(Array|Object|String)*: The value to inspect. #### Returns -*(Number)*: Returns `value.length` if `value` is a string or array, or the number of own enumerable properties if `value` is an object. +*(Number)*: Returns `value.length` or number of own enumerable properties. #### Example -~~~ js +```js _.size([1, 2]); // => 2 @@ -2030,7 +2147,7 @@ _.size({ 'one': 1, 'two': 2, 'three': 3 }); _.size('curly'); // => 5 -~~~ +``` * * * @@ -2040,7 +2157,7 @@ _.size('curly'); ### `_.some(collection [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1120 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2250 "View in source") [Ⓣ][1] Checks if the `callback` returns a truthy value for **any** element of a `collection`. The function returns as soon as it finds passing value, and does not iterate over the entire `collection`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. @@ -2050,13 +2167,13 @@ Checks if the `callback` returns a truthy value for **any** element of a `collec 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Boolean)*: Returns `true` if any value passes the callback check, else `false`. +*(Boolean)*: Returns `true` if any element passes the callback check, else `false`. #### Example -~~~ js +```js _.some([null, 0, 'yes', false]); // => true -~~~ +``` * * * @@ -2066,9 +2183,9 @@ _.some([null, 0, 'yes', false]); ### `_.sortBy(collection, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1152 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2280 "View in source") [Ⓣ][1] -Produces a new sorted array, sorted in ascending order by the results of running each element of `collection` through a transformation `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to sort by *(e.g. 'length')*. +Creates a new sorted array, sorted in ascending order by the results of running each element of `collection` through a `callback`. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index|key, collection)*. The `callback` argument may also be the name of a property to sort by *(e.g. 'length')*. #### Arguments 1. `collection` *(Array|Object|String)*: The collection to iterate over. @@ -2076,10 +2193,10 @@ Produces a new sorted array, sorted in ascending order by the results of running 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Returns -*(Array)*: Returns a new array of sorted values. +*(Array)*: Returns a new array of sorted elements. #### Example -~~~ js +```js _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); // => [3, 1, 2] @@ -2088,7 +2205,7 @@ _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); _.sortBy(['larry', 'brendan', 'moe'], 'length'); // => ['moe', 'larry', 'brendan'] -~~~ +``` * * * @@ -2098,7 +2215,7 @@ _.sortBy(['larry', 'brendan', 'moe'], 'length'); ### `_.sortedIndex(array, value [, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1753 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2913 "View in source") [Ⓣ][1] Uses a binary search to determine the smallest index at which the `value` should be inserted into `array` in order to maintain the sort order of the sorted `array`. If `callback` is passed, it will be executed for `value` and each element in `array` to compute their sort ranking. The `callback` is bound to `thisArg` and invoked with `1` argument; *(value)*. @@ -2112,7 +2229,7 @@ Uses a binary search to determine the smallest index at which the `value` should *(Number)*: Returns the index at which the value should be inserted into `array`. #### Example -~~~ js +```js _.sortedIndex([20, 30, 40], 35); // => 2 @@ -2129,7 +2246,7 @@ _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { return this.wordToNumber[word]; }, dict); // => 2 -~~~ +``` * * * @@ -2139,19 +2256,19 @@ _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { ### `_.tap(value, interceptor)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3560 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3998 "View in source") [Ⓣ][1] Invokes `interceptor` with the `value` as the first argument, and then returns `value`. The purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain. #### Arguments -1. `value` *(Mixed)*: The value to pass to `callback`. +1. `value` *(Mixed)*: The value to pass to `interceptor`. 2. `interceptor` *(Function)*: The function to invoke. #### Returns *(Mixed)*: Returns `value`. #### Example -~~~ js +```js _.chain([1,2,3,200]) .filter(function(num) { return num % 2 == 0; }) .tap(alert) @@ -2159,7 +2276,7 @@ _.chain([1,2,3,200]) .value(); // => // [2, 200] (alerted) // => [4, 40000] -~~~ +``` * * * @@ -2169,9 +2286,9 @@ _.chain([1,2,3,200]) ### `_.template(text, data, options)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3341 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3774 "View in source") [Ⓣ][1] -A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. +A micro-templating method that handles arbitrary delimiters, preserves whitespace, and correctly escapes quotes within interpolated code. Note: For Chrome extensions use the `lodash csp` build and see http://code.google.com/chrome/extensions/trunk/sandboxingEval.html #### Arguments 1. `text` *(String)*: The template text. @@ -2182,15 +2299,15 @@ A micro-templating method that handles arbitrary delimiters, preserves whitespac *(Function, String)*: Returns a compiled function when no `data` object is given, else it returns the interpolated text. #### Example -~~~ js +```js // using compiled template var compiled = _.template('hello: <%= name %>'); compiled({ 'name': 'moe' }); // => 'hello: moe' var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; -_.template(list, { 'people': ['moe', 'curly', 'larry'] }); -// => '
  • moe
  • curly
  • larry
  • ' +_.template(list, { 'people': ['moe', 'larry', 'curly'] }); +// => '
  • moe
  • larry
  • curly
  • ' var template = _.template('<%- value %>'); template({ 'value': ' -~~~ +``` * * * @@ -2228,7 +2345,7 @@ _.template('<%= data.hasWith %>', { 'hasWith': 'no' }, { 'variable': 'data' }); ### `_.throttle(func, wait)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2361 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3525 "View in source") [Ⓣ][1] Creates a new function that, when executed, will only call the `func` function at most once per every `wait` milliseconds. If the throttled function is invoked more than once during the `wait` timeout, `func` will also be called on the trailing edge of the timeout. Subsequent calls to the throttled function will return the result of the last `func` call. @@ -2240,10 +2357,10 @@ Creates a new function that, when executed, will only call the `func` function a *(Function)*: Returns the new throttled function. #### Example -~~~ js +```js var throttled = _.throttle(updatePosition, 100); jQuery(window).on('scroll', throttled); -~~~ +``` * * * @@ -2253,7 +2370,7 @@ jQuery(window).on('scroll', throttled); ### `_.times(n, callback [, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3476 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3914 "View in source") [Ⓣ][1] Executes the `callback` function `n` times. The `callback` is bound to `thisArg` and invoked with `1` argument; *(index)*. @@ -2263,13 +2380,13 @@ Executes the `callback` function `n` times. The `callback` is bound to `thisArg` 3. `[thisArg]` *(Mixed)*: The `this` binding for the callback. #### Example -~~~ js +```js _.times(3, function() { genie.grantWish(); }); // => calls `genie.grantWish()` 3 times _.times(3, function() { this.grantWish(); }, genie); // => also calls `genie.grantWish()` 3 times -~~~ +``` * * * @@ -2279,7 +2396,7 @@ _.times(3, function() { this.grantWish(); }, genie); ### `_.toArray(collection)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1195 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2317 "View in source") [Ⓣ][1] Converts the `collection`, into an array. Useful for converting the `arguments` object. @@ -2290,10 +2407,10 @@ Converts the `collection`, into an array. Useful for converting the `arguments` *(Array)*: Returns the new converted array. #### Example -~~~ js +```js (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); // => [2, 3, 4] -~~~ +``` * * * @@ -2303,9 +2420,9 @@ Converts the `collection`, into an array. Useful for converting the `arguments` ### `_.union([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1793 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2954 "View in source") [Ⓣ][1] -Computes the union of the passed-in arrays. +Computes the union of the passed-in arrays using strict equality for comparisons, i.e. `===`. #### Arguments 1. `[array1, array2, ...]` *(Array)*: Arrays to process. @@ -2314,10 +2431,10 @@ Computes the union of the passed-in arrays. *(Array)*: Returns a new array of unique values, in order, that are present in one or more of the arrays. #### Example -~~~ js +```js _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); // => [1, 2, 3, 101, 10] -~~~ +``` * * * @@ -2327,9 +2444,9 @@ _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); ### `_.uniq(array [, isSorted=false, callback=identity, thisArg])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1838 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2998 "View in source") [Ⓣ][1] -Produces a duplicate-value-free version of the `array` using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `isSorted` will run a faster algorithm. If `callback` is passed, each value of `array` is passed through a transformation `callback` before uniqueness is computed. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index, array)*. +Creates a duplicate-value-free version of the `array` using strict equality for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` for `isSorted` will run a faster algorithm. If `callback` is passed, each element of `array` is passed through a callback` before uniqueness is computed. The `callback` is bound to `thisArg` and invoked with `3` arguments; *(value, index, array)*. #### Arguments 1. `array` *(Array)*: The array to process. @@ -2341,7 +2458,7 @@ Produces a duplicate-value-free version of the `array` using strict equality for *(Array)*: Returns a duplicate-value-free array. #### Example -~~~ js +```js _.uniq([1, 2, 1, 3, 1]); // => [1, 2, 3] @@ -2353,7 +2470,7 @@ _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); // => [1, 2, 3] -~~~ +``` * * * @@ -2363,7 +2480,7 @@ _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); ### `_.uniqueId([prefix])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3503 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3941 "View in source") [Ⓣ][1] Generates a unique id. If `prefix` is passed, the id will be appended to it. @@ -2374,10 +2491,10 @@ Generates a unique id. If `prefix` is passed, the id will be appended to it. *(Number, String)*: Returns a numeric id if no prefix is passed, else a string id may be returned. #### Example -~~~ js +```js _.uniqueId('contact_'); // => 'contact_104' -~~~ +``` * * * @@ -2387,9 +2504,9 @@ _.uniqueId('contact_'); ### `_.values(object)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3151 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1781 "View in source") [Ⓣ][1] -Produces an array of `object`'s own enumerable property values. +Creates an array composed of the own enumerable property values of `object`. #### Arguments 1. `object` *(Object)*: The object to inspect. @@ -2398,10 +2515,41 @@ Produces an array of `object`'s own enumerable property values. *(Array)*: Returns a new array of property values. #### Example -~~~ js +```js _.values({ 'one': 1, 'two': 2, 'three': 3 }); // => [1, 2, 3] -~~~ +``` + +* * * + + + + + + +### `_.where(collection, properties)` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2354 "View in source") [Ⓣ][1] + +Examines each element in a `collection`, returning an array of all elements that contain the given `properties`. + +#### Arguments +1. `collection` *(Array|Object|String)*: The collection to iterate over. +2. `properties` *(Object)*: The object of properties/values to filter by. + +#### Returns +*(Array)*: Returns a new array of elements that contain the given `properties`. + +#### Example +```js +var stooges = [ + { 'name': 'moe', 'age': 40 }, + { 'name': 'larry', 'age': 50 }, + { 'name': 'curly', 'age': 60 } +]; + +_.where(stooges, { 'age': 40 }); +// => [{ 'name': 'moe', 'age': 40 }] +``` * * * @@ -2411,9 +2559,9 @@ _.values({ 'one': 1, 'two': 2, 'three': 3 }); ### `_.without(array [, value1, value2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1887 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3047 "View in source") [Ⓣ][1] -Produces a new array with all occurrences of the passed values removed using strict equality for comparisons, i.e. `===`. +Creates a new array with all occurrences of the passed values removed using strict equality for comparisons, i.e. `===`. #### Arguments 1. `array` *(Array)*: The array to filter. @@ -2423,10 +2571,10 @@ Produces a new array with all occurrences of the passed values removed using str *(Array)*: Returns a new filtered array. #### Example -~~~ js +```js _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); // => [2, 3, 4] -~~~ +``` * * * @@ -2435,28 +2583,27 @@ _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); -### `_.wrap(func, wrapper [, arg1, arg2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L2413 "View in source") [Ⓣ][1] +### `_.wrap(value, wrapper)` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3576 "View in source") [Ⓣ][1] -Create a new function that passes the `func` function to the `wrapper` function as its first argument. Additional arguments are appended to those passed to the `wrapper` function. +Creates a new function that passes `value` to the `wrapper` function as its first argument. Additional arguments passed to the new function are appended to those passed to the `wrapper` function. #### Arguments -1. `func` *(Function)*: The function to wrap. +1. `value` *(Mixed)*: The value to wrap. 2. `wrapper` *(Function)*: The wrapper function. -3. `[arg1, arg2, ...]` *(Mixed)*: Arguments to append to those passed to the wrapper. #### Returns *(Function)*: Returns the new function. #### Example -~~~ js +```js var hello = function(name) { return 'hello: ' + name; }; hello = _.wrap(hello, function(func) { return 'before, ' + func('moe') + ', after'; }); hello(); // => 'before, hello: moe, after' -~~~ +``` * * * @@ -2466,21 +2613,21 @@ hello(); ### `_.zip([array1, array2, ...])` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1920 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3080 "View in source") [Ⓣ][1] -Merges the elements of each array at their corresponding indexes. Useful for separate data sources that are coordinated through matching array indexes. For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix in a similar fashion. +Groups the elements of each array at their corresponding indexes. Useful for separate data sources that are coordinated through matching array indexes. For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix in a similar fashion. #### Arguments 1. `[array1, array2, ...]` *(Array)*: Arrays to process. #### Returns -*(Array)*: Returns a new array of merged arrays. +*(Array)*: Returns a new array of grouped elements. #### Example -~~~ js +```js _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] -~~~ +``` * * * @@ -2489,22 +2636,23 @@ _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); -### `_.zipObject(keys)` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L1949 "View in source") [Ⓣ][1] +### `_.zipObject(keys [, values=[]])` +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3109 "View in source") [Ⓣ][1] -Merges an array of `keys` and an array of `values` into a single object. +Creates an object composed from an array of `keys` and an array of `values`. #### Arguments 1. `keys` *(Array)*: The array of keys. +2. `[values=[]]` *(Array)*: The array of values. #### Returns *(Object)*: Returns an object composed of the given keys and corresponding values. #### Example -~~~ js +```js _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); // => { 'moe': 30, 'larry': 40, 'curly': 50 } -~~~ +``` * * * @@ -2521,7 +2669,7 @@ _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); ### `_.prototype.chain()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3578 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4016 "View in source") [Ⓣ][1] Enables method chaining on the wrapper object. @@ -2529,10 +2677,10 @@ Enables method chaining on the wrapper object. *(Mixed)*: Returns the wrapper object. #### Example -~~~ js +```js _([1, 2, 3]).value(); // => [1, 2, 3] -~~~ +``` * * * @@ -2542,7 +2690,7 @@ _([1, 2, 3]).value(); ### `_.prototype.value()` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L3595 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L4033 "View in source") [Ⓣ][1] Extracts the wrapped value. @@ -2550,10 +2698,10 @@ Extracts the wrapped value. *(Mixed)*: Returns the wrapped value. #### Example -~~~ js +```js _([1, 2, 3]).value(); // => [1, 2, 3] -~~~ +``` * * * @@ -2570,9 +2718,9 @@ _([1, 2, 3]).value(); ### `_.templateSettings` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L220 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L278 "View in source") [Ⓣ][1] -*(Object)*: By default, Lo-Dash uses embedded Ruby *(ERB)* style template delimiters, change the following template settings to use alternative delimiters. +*(Object)*: 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. * * * @@ -2582,7 +2730,7 @@ _([1, 2, 3]).value(); ### `_.templateSettings.escape` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L229 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L287 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect `data` property values to be HTML-escaped. @@ -2594,7 +2742,7 @@ _([1, 2, 3]).value(); ### `_.templateSettings.evaluate` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L238 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L296 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect code to be evaluated. @@ -2606,7 +2754,7 @@ _([1, 2, 3]).value(); ### `_.templateSettings.interpolate` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L247 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L305 "View in source") [Ⓣ][1] *(RegExp)*: Used to detect `data` property values to inject. @@ -2618,7 +2766,7 @@ _([1, 2, 3]).value(); ### `_.templateSettings.variable` -# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L256 "View in source") [Ⓣ][1] +# [Ⓢ](https://github.com/bestiejs/lodash/blob/master/lodash.js#L314 "View in source") [Ⓣ][1] *(String)*: Used to reference the data object in the template text. @@ -2633,4 +2781,4 @@ _([1, 2, 3]).value(); - [1]: #toc "Jump back to the TOC." \ No newline at end of file + [1]: #_ "Jump back to the TOC." \ No newline at end of file diff --git a/doc/parse.php b/doc/parse.php index aa7e45a98e..b059ce7e95 100644 --- a/doc/parse.php +++ b/doc/parse.php @@ -21,7 +21,7 @@ // generate Markdown $markdown = docdown(array( 'path' => '../' . $file, - 'title' => 'Lo-Dash v0.4.2', + 'title' => 'Lo-Dash v0.5.0', 'url' => 'https://github.com/bestiejs/lodash/blob/master/lodash.js' )); diff --git a/lodash.js b/lodash.js index af689d3aa7..5981229720 100644 --- a/lodash.js +++ b/lodash.js @@ -1,5 +1,5 @@ /*! - * Lo-Dash v0.4.2 + * Lo-Dash v0.5.0 * Copyright 2012 John-David Dalton * Based on Underscore.js 1.3.3, copyright 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. * @@ -39,7 +39,10 @@ /** Native prototype shortcuts */ var ArrayProto = Array.prototype, - ObjectProto = Object.prototype; + BoolProto = Boolean.prototype, + ObjectProto = Object.prototype, + NumberProto = Number.prototype, + StringProto = String.prototype; /** Used to generate unique IDs */ var idCounter = 0; @@ -55,6 +58,9 @@ reEmptyStringMiddle = /\b(__p \+=) '' \+/g, reEmptyStringTrailing = /(__e\(.*?\)|\b__t\)) \+\n'';/g; + /** Used to match regexp flags from their coerced string values */ + var reFlags = /\w*$/; + /** Used to insert the data object variable into compiled template source */ var reInsertVariable = /(?:__e|__t = )\(\s*(?![\d\s"']|this\.)/g; @@ -104,11 +110,13 @@ nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys; /** `Object#toString` result shortcuts */ - var arrayClass = '[object Array]', + var argsClass = '[object Arguments]', + arrayClass = '[object Array]', boolClass = '[object Boolean]', dateClass = '[object Date]', funcClass = '[object Function]', numberClass = '[object Number]', + objectClass = '[object Object]', regexpClass = '[object RegExp]', stringClass = '[object String]'; @@ -121,7 +129,26 @@ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are * made non-enumerable as well. */ - var hasDontEnumBug = !propertyIsEnumerable.call({ 'valueOf': 0 }, 'valueOf'); + var hasDontEnumBug; + + /** Detect if own properties are iterated after inherited properties (IE < 9) */ + var iteratesOwnLast; + + /** Detect if an `arguments` object's indexes are non-enumerable (IE < 9) */ + var noArgsEnum = true; + + (function() { + var props = []; + function ctor() { this.x = 1; } + ctor.prototype = { 'valueOf': 1, 'y': 1 }; + for (var prop in new ctor) { props.push(prop); } + for (prop in arguments) { noArgsEnum = !prop; } + hasDontEnumBug = (props + '').length < 4; + iteratesOwnLast = props[0] != 'x'; + }(1)); + + /** Detect if an `arguments` object's [[Class]] is unresolvable (Firefox < 4, IE < 9) */ + var noArgsClass = !isArguments(arguments); /** Detect if `Array#slice` cannot be used to convert strings to arrays (Opera < 10.52) */ var noArraySliceOnStrings = slice.call('x')[0] != 'x'; @@ -133,18 +160,47 @@ */ var noCharByIndex = ('x'[0] + Object('x')[0]) != 'xx'; + /** + * Detect if a node's [[Class]] is unresolvable (IE < 9) + * and that the JS engine won't error when attempting to coerce an object to + * a string without a `toString` property value of `typeof` "function". + */ + try { + var noNodeClass = ({ 'toString': 0 } + '', toString.call(window.document || 0) == objectClass); + } catch(e) { } + /* Detect if `Function#bind` exists and is inferred to be fast (all but V8) */ var isBindFast = nativeBind && /\n|Opera/.test(nativeBind + toString.call(window.opera)); - /* Detect if `Object.keys` exists and is inferred to be fast (V8, Opera, IE) */ + /* Detect if `Object.keys` exists and is inferred to be fast (IE, Opera, V8) */ var isKeysFast = nativeKeys && /^.+$|true/.test(nativeKeys + !!window.attachEvent); /** Detect if sourceURL syntax is usable without erroring */ try { - // Adobe's and Narwhal's JS engines will error - var useSourceURL = (Function('//@')(), true); + // The JS engine in Adobe products, like InDesign, will throw a syntax error + // when it encounters a single line comment beginning with the `@` symbol. + // The JS engine in Narwhal will generate the function `function anonymous(){//}` + // and throw a syntax error. In IE, `@` symbols are part of its non-standard + // conditional compilation support. The `@cc_on` statement activates its support + // while the trailing ` !` induces a syntax error to exlude it. Compatibility + // modes in IE > 8 require a space before the `!` to induce a syntax error. + // See http://msdn.microsoft.com/en-us/library/121hztk3(v=vs.94).aspx + var useSourceURL = (Function('//@cc_on !')(), true); } catch(e){ } + /** Used to identify object classifications that are array-like */ + var arrayLikeClasses = {}; + arrayLikeClasses[boolClass] = arrayLikeClasses[dateClass] = arrayLikeClasses[funcClass] = + arrayLikeClasses[numberClass] = arrayLikeClasses[objectClass] = arrayLikeClasses[regexpClass] = false; + arrayLikeClasses[argsClass] = arrayLikeClasses[arrayClass] = arrayLikeClasses[stringClass] = true; + + /** Used to identify object classifications that `_.clone` supports */ + var cloneableClasses = {}; + cloneableClasses[argsClass] = cloneableClasses[funcClass] = false; + cloneableClasses[arrayClass] = cloneableClasses[boolClass] = cloneableClasses[dateClass] = + cloneableClasses[numberClass] = cloneableClasses[objectClass] = cloneableClasses[regexpClass] = + cloneableClasses[stringClass] = true; + /** * Used to escape characters for inclusion in HTML. * The `>` and `/` characters don't require escaping in HTML and have no @@ -165,7 +221,8 @@ 'object': true, 'number': false, 'string': false, - 'undefined': false + 'undefined': false, + 'unknown': true }; /** Used to escape characters for inclusion in compiled string literals */ @@ -210,8 +267,9 @@ } /** - * By default, Lo-Dash uses embedded Ruby (ERB) style template delimiters, - * 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 _ @@ -267,10 +325,10 @@ */ var iteratorTemplate = template( // conditional strict mode - '<% if (useStrict) { %>\'use strict\';\n<% } %>' + + '<% if (useStrict) { %>\'use strict\';\n<% } %>' + // the `iteratee` may be reassigned by the `top` snippet - 'var index, iteratee = <%= firstArg %>, ' + + 'var index, value, iteratee = <%= firstArg %>, ' + // assign the `result` variable an initial value 'result<% if (init) { %> = <%= init %><% } %>;\n' + // add code to exit early or do so if the first argument is falsey @@ -281,7 +339,7 @@ // the following branch is for iterating arrays and array-like objects '<% if (arrayBranch) { %>' + 'var length = iteratee.length; index = -1;' + - ' <% if (objectBranch) { %>\nif (length === length >>> 0) {<% } %>' + + ' <% if (objectBranch) { %>\nif (length > -1 && length === length >>> 0) {<% } %>' + // add support for accessing string characters by index if needed ' <% if (noCharByIndex) { %>\n' + @@ -292,6 +350,7 @@ ' <%= arrayBranch.beforeLoop %>;\n' + ' while (++index < length) {\n' + + ' value = iteratee[index];\n' + ' <%= arrayBranch.inLoop %>\n' + ' }' + ' <% if (objectBranch) { %>\n}<% } %>' + @@ -299,7 +358,19 @@ // the following branch is for iterating an object's own/inherited properties '<% if (objectBranch) { %>' + - ' <% if (arrayBranch) { %>\nelse {<% } %>' + + ' <% if (arrayBranch) { %>\nelse {' + + + // add support for iterating over `arguments` objects if needed + ' <% } else if (noArgsEnum) { %>\n' + + ' var length = iteratee.length; index = -1;\n' + + ' if (length && isArguments(iteratee)) {\n' + + ' while (++index < length) {\n' + + ' value = iteratee[index += \'\'];\n' + + ' <%= objectBranch.inLoop %>\n' + + ' }\n' + + ' } else {' + + ' <% } %>' + + ' <% if (!hasDontEnumBug) { %>\n' + ' var skipProto = typeof iteratee == \'function\' && \n' + ' propertyIsEnumerable.call(iteratee, \'prototype\');\n' + @@ -307,13 +378,14 @@ // iterate own properties using `Object.keys` if it's fast ' <% if (isKeysFast && useHas) { %>\n' + - ' var props = nativeKeys(iteratee),\n' + - ' propIndex = -1,\n' + - ' length = props.length;\n\n' + + ' var ownIndex = -1,\n' + + ' ownProps = nativeKeys(iteratee),\n' + + ' length = ownProps.length;\n\n' + ' <%= objectBranch.beforeLoop %>;\n' + - ' while (++propIndex < length) {\n' + - ' index = props[propIndex];\n' + + ' while (++ownIndex < length) {\n' + + ' index = ownProps[ownIndex];\n' + ' if (!(skipProto && index == \'prototype\')) {\n' + + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' }\n' + ' }' + @@ -324,6 +396,7 @@ ' for (index in iteratee) {' + ' <% if (hasDontEnumBug) { %>\n' + ' <% if (useHas) { %>if (hasOwnProperty.call(iteratee, index)) {\n <% } %>' + + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>;\n' + ' <% if (useHas) { %>}<% } %>' + ' <% } else { %>\n' + @@ -336,6 +409,7 @@ // [[Enumerable]] value. ' if (!(skipProto && index == \'prototype\')<% if (useHas) { %> &&\n' + ' hasOwnProperty.call(iteratee, index)<% } %>) {\n' + + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' }' + ' <% } %>\n' + @@ -354,11 +428,12 @@ ' if (shadowed[k] == \'constructor\') {' + ' %>!(ctor && ctor.prototype === iteratee) && <%' + ' } %>hasOwnProperty.call(iteratee, index)) {\n' + + ' value = iteratee[index];\n' + ' <%= objectBranch.inLoop %>\n' + ' }' + ' <% } %>' + ' <% } %>' + - ' <% if (arrayBranch) { %>\n}<% } %>' + + ' <% if (arrayBranch || noArgsEnum) { %>\n}<% } %>' + '<% } %>\n' + // add code to the bottom of the iteration function @@ -382,13 +457,30 @@ 'else if (thisArg) {\n' + ' callback = iteratorBind(callback, thisArg)\n' + '}', - 'inLoop': 'callback(iteratee[index], index, collection)' + 'inLoop': 'if (callback(value, index, collection) === false) return result' + }; + + /** Reusable iterator options for `countBy`, `groupBy`, and `sortBy` */ + var countByIteratorOptions = { + 'init': '{}', + 'top': + 'var prop;\n' + + 'if (typeof callback != \'function\') {\n' + + ' var valueProp = callback;\n' + + ' callback = function(value) { return value[valueProp] }\n' + + '}\n' + + 'else if (thisArg) {\n' + + ' callback = iteratorBind(callback, thisArg)\n' + + '}', + 'inLoop': + 'prop = callback(value, index, collection);\n' + + '(hasOwnProperty.call(result, prop) ? result[prop]++ : result[prop] = 1)' }; /** Reusable iterator options for `every` and `some` */ var everyIteratorOptions = { 'init': 'true', - 'inLoop': 'if (!callback(iteratee[index], index, collection)) return !result' + 'inLoop': 'if (!callback(value, index, collection)) return !result' }; /** Reusable iterator options for `defaults` and `extend` */ @@ -398,17 +490,16 @@ 'args': 'object', 'init': 'object', 'top': - 'for (var iterateeIndex = 1, length = arguments.length; iterateeIndex < length; iterateeIndex++) {\n' + - ' iteratee = arguments[iterateeIndex];\n' + - (hasDontEnumBug ? ' if (iteratee) {' : ''), - 'inLoop': 'result[index] = iteratee[index]', - 'bottom': (hasDontEnumBug ? ' }\n' : '') + '}' + 'for (var argsIndex = 1, argsLength = arguments.length; argsIndex < argsLength; argsIndex++) {\n' + + ' if (iteratee = arguments[argsIndex]) {', + 'inLoop': 'result[index] = value', + 'bottom': ' }\n}' }; - /** Reusable iterator options for `filter` and `reject` */ + /** Reusable iterator options for `filter`, `reject`, and `where` */ var filterIteratorOptions = { 'init': '[]', - 'inLoop': 'callback(iteratee[index], index, collection) && result.push(iteratee[index])' + 'inLoop': 'callback(value, index, collection) && result.push(value)' }; /** Reusable iterator options for `find`, `forEach`, `forIn`, and `forOwn` */ @@ -432,8 +523,8 @@ 'object': 'result = ' + (isKeysFast ? 'Array(length)' : '[]') }, 'inLoop': { - 'array': 'result[index] = callback(iteratee[index], index, collection)', - 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(callback(iteratee[index], index, collection))' + 'array': 'result[index] = callback(value, index, collection)', + 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(callback(value, index, collection))' } }; @@ -554,6 +645,7 @@ data.firstArg = firstArg; data.hasDontEnumBug = hasDontEnumBug; data.isKeysFast = isKeysFast; + data.noArgsEnum = noArgsEnum; data.shadowed = shadowed; data.useHas = data.useHas !== false; data.useStrict = data.useStrict !== false; @@ -569,29 +661,35 @@ } // create the function factory var factory = Function( - 'arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, ' + - 'iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, ' + - 'stringClass, toString', - 'return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' + 'arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, ' + + 'hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, ' + + 'isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, ' + + 'propertyIsEnumerable, slice, stringClass, toString', + 'var callee = function(' + args + ') {\n' + iteratorTemplate(data) + '\n};\n' + + 'return callee' ); // return the compiled function return factory( - arrayClass, bind, compareAscending, funcClass, hasOwnProperty, identity, - iteratorBind, objectTypes, nativeKeys, propertyIsEnumerable, slice, - stringClass, toString + arrayLikeClasses, ArrayProto, bind, compareAscending, concat, forIn, + hasOwnProperty, identity, indexOf, isArguments, isArray, isFunction, + isPlainObject, iteratorBind, objectClass, objectTypes, nativeKeys, + propertyIsEnumerable, slice, stringClass, toString ); } /** - * Used by `sortBy` to compare transformed values of `collection`, sorting - * them in ascending order. + * Used by `sortBy` to compare transformed `collection` values, sorting them + * stabily 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 `-1` if `a` < `b`, `0` if `a` == `b`, or `1` if `a` > `b`. + * @returns {Number} Returns the sort order indicator of `1` or `-1`. */ function compareAscending(a, b) { + var ai = a.index, + bi = b.index; + a = a.criteria; b = b.criteria; @@ -601,7 +699,9 @@ if (b === undefined) { return -1; } - return a < b ? -1 : a > b ? 1 : 0; + // ensure a stable sort in V8 and other engines + // http://code.google.com/p/v8/issues/detail?id=90 + return a < b ? -1 : a > b ? 1 : ai < bi ? -1 : 1; } /** @@ -639,6 +739,51 @@ return htmlEscapes[match]; } + /** + * 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. + * + * @private + * @param {Mixed} value The value to check. + * @param {Boolean} [skipArgsCheck=false] Internally used to skip checks for + * `arguments` objects. + * @returns {Boolean} Returns `true` if the `value` is a plain `Object` object, + * else `false`. + */ + function isPlainObject(value, skipArgsCheck) { + // avoid non-objects and false positives for `arguments` objects + var result = false; + if (!(value && typeof value == 'object') || (!skipArgsCheck && isArguments(value))) { + return result; + } + // IE < 9 presents DOM nodes as `Object` objects except they have `toString` + // methods that are `typeof` "string" and still can coerce nodes to strings. + // Also check that the constructor is `Object` (i.e. `Object instanceof Object`) + var ctor = value.constructor; + if ((!noNodeClass || !(typeof value.toString != 'function' && typeof (value + '') == 'string')) && + (!isFunction(ctor) || ctor instanceof ctor)) { + // IE < 9 iterates inherited properties before own properties. If the first + // iterated property is an object's own property then there are no inherited + // enumerable properties. + if (iteratesOwnLast) { + forIn(value, function(objValue, objKey) { + result = !hasOwnProperty.call(value, objKey); + return false; + }); + return result === false; + } + // In most environments an object's own properties are iterated before + // its inherited properties. If the last iterated property is an object's + // own property then there are no inherited enumerable properties. + forIn(value, function(objValue, objKey) { + result = objKey; + }); + return result === false || hasOwnProperty.call(value, result); + } + return result; + } + /** * Creates a new function that, when called, invokes `func` with the `this` * binding of `thisArg` and the arguments (value, index, object). @@ -663,21 +808,6 @@ // no operation performed } - /** - * A shim implementation of `Object.keys` that produces an array of the given - * object's own enumerable property names. - * - * @private - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. - */ - var shimKeys = createIterator({ - 'args': 'object', - 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', - 'init': '[]', - 'inLoop': 'result.push(index)' - }); - /** * Used by `template` to replace "escape" template delimiters with tokens. * @@ -701,17 +831,17 @@ * * @private * @param {String} match The matched template delimiter. - * @param {String} value The delimiter value. - * @param {String} escapeValue The "escape" delimiter value. - * @param {String} interpolateValue The "interpolate" delimiter value. + * @param {String} escapeValue The complex "escape" delimiter value. + * @param {String} interpolateValue The complex "interpolate" delimiter value. + * @param {String} [evaluateValue] The "evaluate" delimiter value. * @returns {String} Returns a token. */ - function tokenizeEvaluate(match, value, escapeValue, interpolateValue) { + function tokenizeEvaluate(match, escapeValue, interpolateValue, evaluateValue) { var index = tokenized.length; - if (value) { - tokenized[index] = "';\n" + value + ";\n__p += '" - } else if (escapeValue) { + if (escapeValue) { tokenized[index] = "' +\n__e(" + escapeValue + ") +\n'"; + } else if (evaluateValue) { + tokenized[index] = "';\n" + evaluateValue + ";\n__p += '"; } else if (interpolateValue) { tokenized[index] = "' +\n((__t = (" + interpolateValue + ")) == null ? '' : __t) +\n'"; } @@ -738,2421 +868,2720 @@ /*--------------------------------------------------------------------------*/ /** - * Checks if a given `target` value is present in a `collection` using strict - * equality for comparisons, i.e. `===`. + * Checks if `value` is an `arguments` object. * * @static * @memberOf _ - * @alias include - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Mixed} target The value to check for. - * @returns {Boolean} Returns `true` if `target` value is found, else `false`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. * @example * - * _.contains([1, 2, 3], 3); - * // => true - * - * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * (function() { return _.isArguments(arguments); })(1, 2, 3); * // => true * - * _.contains('curly', 'ur'); - * // => true + * _.isArguments([1, 2, 3]); + * // => false */ - var contains = createIterator({ - 'args': 'collection, target', - 'init': 'false', - 'noCharByIndex': false, - 'beforeLoop': { - 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' - }, - 'inLoop': 'if (iteratee[index] === target) return true' - }); + function isArguments(value) { + return toString.call(value) == argsClass; + } + // fallback for browsers that can't detect `arguments` objects by [[Class]] + if (noArgsClass) { + isArguments = function(value) { + return !!(value && hasOwnProperty.call(value, 'callee')); + }; + } /** - * Checks if the `callback` returns a truthy value for **all** elements of a - * `collection`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, index|key, collection). + * Checks if `value` is an array. * * @static * @memberOf _ - * @alias all - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Boolean} Returns `true` if all values pass the callback check, else `false`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. * @example * - * _.every([true, 1, null, 'yes'], Boolean); + * (function() { return _.isArray(arguments); })(); * // => false + * + * _.isArray([1, 2, 3]); + * // => true */ - var every = createIterator(baseIteratorOptions, everyIteratorOptions); + var isArray = nativeIsArray || function(value) { + return toString.call(value) == arrayClass; + }; /** - * Examines each value in a `collection`, returning an array of all values the - * `callback` returns truthy for. The `callback` is bound to `thisArg` and - * invoked with 3 arguments; (value, index|key, collection). + * Checks if `value` is a function. * * @static * @memberOf _ - * @alias select - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values that passed callback check. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. * @example * - * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [2, 4, 6] + * _.isFunction(''.concat); + * // => true */ - var filter = createIterator(baseIteratorOptions, filterIteratorOptions); + function isFunction(value) { + return typeof value == 'function'; + } + // fallback for older versions of Chrome and Safari + if (isFunction(/x/)) { + isFunction = function(value) { + return toString.call(value) == funcClass; + }; + } /** - * Examines each value in a `collection`, returning the first one the `callback` - * returns truthy for. The function returns as soon as it finds an acceptable - * value, and does not iterate over the entire `collection`. The `callback` is - * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). - * - * @static - * @memberOf _ - * @alias detect - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the value that passed the callback check, else `undefined`. - * @example + * A shim implementation of `Object.keys` that produces an array of the given + * object's own enumerable property names. * - * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => 2 + * @private + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. */ - var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { - 'init': '', - 'inLoop': 'if (callback(iteratee[index], index, collection)) return iteratee[index]' + var shimKeys = createIterator({ + 'args': 'object', + 'exit': 'if (!(object && objectTypes[typeof object])) throw TypeError()', + 'init': '[]', + 'inLoop': 'result.push(index)' }); + /*--------------------------------------------------------------------------*/ + /** - * Iterates over a `collection`, executing the `callback` for each value in the - * `collection`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, index|key, collection). + * Creates a clone of `value`. If `deep` is `true`, all nested objects will + * also be cloned otherwise they will be assigned by reference. If a value has + * a `clone` method it will be used to perform the clone. Functions, DOM nodes, + * `arguments` objects, and objects created by constructors other than `Object` + * are **not** cloned unless they have a custom `clone` method. * * @static * @memberOf _ - * @alias each - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array|Object} Returns the `collection`. + * @category Objects + * @param {Mixed} value The value to clone. + * @param {Boolean} deep A flag to indicate a deep clone. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `deep`. + * @param {Array} [stack=[]] Internally used to keep track of traversed objects + * to avoid circular references. + * @param {Object} thorough Internally used to indicate whether or not to perform + * a more thorough clone of non-object values. + * @returns {Mixed} Returns the cloned `value`. * @example * - * _([1, 2, 3]).forEach(alert).join(','); - * // => alerts each number and returns '1,2,3' + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; * - * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number (order is not guaranteed) + * _.clone({ 'name': 'moe' }); + * // => { 'name': 'moe' } + * + * var shallow = _.clone(stooges); + * shallow[0] === stooges[0]; + * // => true + * + * var deep = _.clone(stooges, true); + * shallow[0] === stooges[0]; + * // => false */ - var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + function clone(value, deep, guard, stack, thorough) { + if (value == null) { + return value; + } + if (guard) { + deep = false; + } + // avoid slower checks on primitives + thorough || (thorough = { 'value': null }); + if (thorough.value == null) { + // primitives passed from iframes use the primary document's native prototypes + thorough.value = !!(BoolProto.clone || NumberProto.clone || StringProto.clone); + } + // use custom `clone` method if available + var isObj = objectTypes[typeof value]; + if ((isObj || thorough.value) && value.clone && isFunction(value.clone)) { + thorough.value = null; + return value.clone(deep); + } + // inspect [[Class]] + if (isObj) { + // don't clone `arguments` objects, functions, or non-object Objects + var className = toString.call(value); + if (!cloneableClasses[className] || (noArgsClass && isArguments(value))) { + return value; + } + var isArr = className == arrayClass; + isObj = isArr || (className == objectClass ? isPlainObject(value, true) : isObj); + } + // shallow clone + if (!isObj || !deep) { + // don't clone functions + return isObj + ? (isArr ? slice.call(value) : extend({}, value)) + : value; + } + + var ctor = value.constructor; + switch (className) { + case boolClass: + return new ctor(value == true); + + case dateClass: + return new ctor(+value); + + case numberClass: + case stringClass: + return new ctor(value); + + case regexpClass: + return ctor(value.source, reFlags.exec(value)); + } + + // check for circular references and return corresponding clone + stack || (stack = []); + var length = stack.length; + while (length--) { + if (stack[length].source == value) { + return stack[length].value; + } + } + + // init cloned object + length = value.length; + var result = isArr ? ctor(length) : {}; + + // add current clone and original source value to the stack of traversed objects + stack.push({ 'value': result, 'source': value }); + + // recursively populate clone (susceptible to call stack limits) + if (isArr) { + var index = -1; + while (++index < length) { + result[index] = clone(value[index], deep, null, stack, thorough); + } + } else { + forOwn(value, function(objValue, key) { + result[key] = clone(objValue, deep, null, stack, thorough); + }); + } + return result; + } /** - * Splits `collection` into sets, grouped by the result of running each value - * through `callback`. The `callback` is bound to `thisArg` and invoked with - * 3 arguments; (value, index|key, collection). The `callback` argument may - * also be the name of a property to group by. + * Assigns enumerable properties of the default object(s) to the `destination` + * object for all `destination` properties that resolve to `null`/`undefined`. + * Once a property is set, additional defaults of the same property will be + * ignored. * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to group by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns an object of grouped values. + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [default1, default2, ...] The default objects. + * @returns {Object} Returns the destination object. * @example * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); - * // => { '1': [1.3], '2': [2.1, 2.4] } + * var iceCream = { 'flavor': 'chocolate' }; + * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); + * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + */ + var defaults = createIterator(extendIteratorOptions, { + 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop + }); + + /** + * Creates a shallow clone of `object` excluding the specified properties. + * Property names may be specified as individual arguments or as arrays of + * property names. * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); - * // => { '1': [1.3], '2': [2.1, 2.4] } + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The source object. + * @param {Object} [prop1, prop2, ...] The properties to drop. + * @returns {Object} Returns an object without the dropped properties. + * @example * - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } + * _.drop({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'userid'); + * // => { 'name': 'moe', 'age': 40 } */ - var groupBy = createIterator(baseIteratorOptions, { + var drop = createIterator({ + 'useHas': false, + 'args': 'object', 'init': '{}', - 'top': - 'var prop, isFunc = typeof callback == \'function\';\n' + - 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', - 'inLoop': - 'prop = isFunc\n' + - ' ? callback(iteratee[index], index, collection)\n' + - ' : iteratee[index][callback];\n' + - '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(iteratee[index])' + 'top': 'var props = concat.apply(ArrayProto, arguments)', + 'inLoop': 'if (indexOf(props, index) < 0) result[index] = value' }); /** - * Invokes the method named by `methodName` on each element in the `collection`. - * Additional arguments will be passed to each invoked method. If `methodName` - * is a function it will be invoked for, and `this` bound to, each element - * in the `collection`. + * Assigns enumerable properties of the source object(s) to the `destination` + * object. Subsequent sources will overwrite propery assignments of previous + * sources. * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} methodName The name of the method to invoke or - * the function invoked per iteration. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. - * @returns {Array} Returns a new array of values returned from each invoked method. + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @returns {Object} Returns the destination object. * @example * - * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); - * // => [[1, 5, 7], [1, 2, 3]] - * - * _.invoke([123, 456], String.prototype.split, ''); - * // => [['1', '2', '3'], ['4', '5', '6']] + * _.extend({ 'name': 'moe' }, { 'age': 40 }); + * // => { 'name': 'moe', 'age': 40 } */ - var invoke = createIterator(mapIteratorOptions, { - 'args': 'collection, methodName', - 'top': - 'var args = slice.call(arguments, 2),\n' + - ' isFunc = typeof methodName == \'function\'', - 'inLoop': { - 'array': - 'result[index] = (isFunc ? methodName : iteratee[index][methodName])' + - '.apply(iteratee[index], args)', - 'object': - 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + - '((isFunc ? methodName : iteratee[index][methodName]).apply(iteratee[index], args))' - } - }); + var extend = createIterator(extendIteratorOptions); /** - * Produces a new array of values by mapping each element in the `collection` - * through a transformation `callback`. The `callback` is bound to `thisArg` - * and invoked with 3 arguments; (value, index|key, collection). + * Iterates over `object`'s own and inherited enumerable properties, executing + * the `callback` for each property. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, key, object). Callbacks may exit iteration + * early by explicitly returning `false`. * * @static * @memberOf _ - * @alias collect - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values returned by the callback. + * @returns {Object} Returns `object`. * @example * - * _.map([1, 2, 3], function(num) { return num * 3; }); - * // => [3, 6, 9] + * function Dog(name) { + * this.name = name; + * } * - * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); - * // => [3, 6, 9] (order is not guaranteed) + * Dog.prototype.bark = function() { + * alert('Woof, woof!'); + * }; + * + * _.forIn(new Dog('Dagny'), function(value, key) { + * alert(key); + * }); + * // => alerts 'name' and 'bark' (order is not guaranteed) */ - var map = createIterator(baseIteratorOptions, mapIteratorOptions); + var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { + 'useHas': false + }); /** - * Retrieves the value of a specified property from all elements in - * the `collection`. + * Iterates over `object`'s own enumerable properties, executing the `callback` + * for each property. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, key, object). Callbacks may exit iteration early by + * explicitly returning `false`. * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {String} property The property to pluck. - * @returns {Array} Returns a new array of property values. + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns `object`. * @example * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; - * - * _.pluck(stooges, 'name'); - * // => ['moe', 'larry', 'curly'] + * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * alert(key); + * }); + * // => alerts '0', '1', and 'length' (order is not guaranteed) */ - var pluck = createIterator(mapIteratorOptions, { - 'args': 'collection, property', - 'inLoop': { - 'array': 'result[index] = iteratee[index][property]', - 'object': 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '(iteratee[index][property])' - } - }); + var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); /** - * Boils down a `collection` to a single value. The initial state of the - * reduction is `accumulator` and each successive step of it should be returned - * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 - * arguments; for arrays they are (accumulator, value, index|key, collection). + * Creates a sorted array of all enumerable properties, own and inherited, + * of `object` that have function values. * * @static * @memberOf _ - * @alias foldl, inject - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the accumulated value. + * @alias methods + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names that have function values. * @example * - * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); - * // => 6 + * _.functions(_); + * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ - var reduce = createIterator({ - 'args': 'collection, callback, accumulator, thisArg', - 'init': 'accumulator', - 'top': - 'var noaccum = arguments.length < 3;\n' + - 'if (thisArg) callback = iteratorBind(callback, thisArg)', - 'beforeLoop': { - 'array': 'if (noaccum) result = collection[++index]' - }, - 'inLoop': { - 'array': - 'result = callback(result, iteratee[index], index, collection)', - 'object': - 'result = noaccum\n' + - ' ? (noaccum = false, iteratee[index])\n' + - ' : callback(result, iteratee[index], index, collection)' - } + var functions = createIterator({ + 'useHas': false, + 'args': 'object', + 'init': '[]', + 'inLoop': 'if (isFunction(value)) result.push(index)', + 'bottom': 'result.sort()' }); /** - * The right-associative version of `_.reduce`. + * Checks if the specified object `property` exists and is a direct property, + * instead of an inherited property. * * @static * @memberOf _ - * @alias foldr - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [accumulator] Initial value of the accumulator. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the accumulated value. + * @category Objects + * @param {Object} object The object to check. + * @param {String} property The property to check for. + * @returns {Boolean} Returns `true` if key is a direct property, else `false`. * @example * - * var list = [[0, 1], [2, 3], [4, 5]]; - * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); - * // => [4, 5, 2, 3, 0, 1] + * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); + * // => true */ - function reduceRight(collection, callback, accumulator, thisArg) { - if (!collection) { - return accumulator; - } - - var length = collection.length, - noaccum = arguments.length < 3; - - if(thisArg) { - callback = iteratorBind(callback, thisArg); - } - if (length === length >>> 0) { - var iteratee = noCharByIndex && toString.call(collection) == stringClass - ? collection.split('') - : collection; - - if (length && noaccum) { - accumulator = iteratee[--length]; - } - while (length--) { - accumulator = callback(accumulator, iteratee[length], length, collection); - } - return accumulator; - } - - var prop, - props = keys(collection); - - length = props.length; - if (length && noaccum) { - accumulator = collection[props[--length]]; - } - while (length--) { - prop = props[length]; - accumulator = callback(accumulator, collection[prop], prop, collection); - } - return accumulator; + function has(object, property) { + return hasOwnProperty.call(object, property); } /** - * The opposite of `_.filter`, this method returns the values of a - * `collection` that `callback` does **not** return truthy for. + * Checks if `value` is a boolean (`true` or `false`) value. * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of values that did **not** pass the callback check. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. * @example * - * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); - * // => [1, 3, 5] + * _.isBoolean(null); + * // => false */ - var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { - 'inLoop': '!' + filterIteratorOptions.inLoop - }); + function isBoolean(value) { + return value === true || value === false || toString.call(value) == boolClass; + } /** - * Checks if the `callback` returns a truthy value for **any** element of a - * `collection`. The function returns as soon as it finds passing value, and - * does not iterate over the entire `collection`. The `callback` is bound to - * `thisArg` and invoked with 3 arguments; (value, index|key, collection). + * Checks if `value` is a date. * * @static * @memberOf _ - * @alias any - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function} [callback=identity] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Boolean} Returns `true` if any value passes the callback check, else `false`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. * @example * - * _.some([null, 0, 'yes', false]); + * _.isDate(new Date); * // => true */ - var some = createIterator(baseIteratorOptions, everyIteratorOptions, { - 'init': 'false', - 'inLoop': everyIteratorOptions.inLoop.replace('!', '') - }); + function isDate(value) { + return toString.call(value) == dateClass; + } + /** + * Checks if `value` is a DOM element. + * + * @static + * @memberOf _ + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @example + * + * _.isElement(document.body); + * // => true + */ + function isElement(value) { + return !!(value && value.nodeType == 1); + } /** - * Produces a new sorted array, sorted in ascending order by the results of - * running each element of `collection` through a transformation `callback`. - * The `callback` is bound to `thisArg` and invoked with 3 arguments; - * (value, index|key, collection). The `callback` argument may also be the - * name of a property to sort by (e.g. 'length'). + * 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". * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to sort by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of sorted values. + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. * @example * - * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); - * // => [3, 1, 2] + * _.isEmpty([1, 2, 3]); + * // => false * - * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); - * // => [3, 1, 2] + * _.isEmpty({}); + * // => true * - * _.sortBy(['larry', 'brendan', 'moe'], 'length'); - * // => ['moe', 'larry', 'brendan'] + * _.isEmpty(''); + * // => true */ - var sortBy = createIterator(baseIteratorOptions, mapIteratorOptions, { + var isEmpty = createIterator({ + 'args': 'value', + 'init': 'true', 'top': - 'if (typeof callback == \'string\') {\n' + - ' var prop = callback;\n' + - ' callback = function(collection) { return collection[prop] }\n' + - '}\n' + - 'else if (thisArg) {\n' + - ' callback = iteratorBind(callback, thisArg)\n' + - '}', + 'var className = toString.call(value),\n' + + ' length = value.length;\n' + + 'if (arrayLikeClasses[className]' + + (noArgsClass ? ' || isArguments(value)' : '') + ' ||\n' + + ' (className == objectClass && length > -1 && length === length >>> 0 &&\n' + + ' isFunction(value.splice))' + + ') return !length', 'inLoop': { - 'array': - 'result[index] = {\n' + - ' criteria: callback(iteratee[index], index, collection),\n' + - ' value: iteratee[index]\n' + - '}', - 'object': - 'result' + (isKeysFast ? '[propIndex] = ' : '.push') + '({\n' + - ' criteria: callback(iteratee[index], index, collection),\n' + - ' value: iteratee[index]\n' + - '})' - }, - 'bottom': - 'result.sort(compareAscending);\n' + - 'length = result.length;\n' + - 'while (length--) {\n' + - ' result[length] = result[length].value\n' + - '}' + 'object': 'return false' + } }); /** - * Converts the `collection`, into an array. Useful for converting the - * `arguments` object. + * Performs a deep comparison between two values to determine if they are + * equivalent to each other. If a value has an `isEqual` method it will be + * used to perform the comparison. * * @static * @memberOf _ - * @category Collections - * @param {Array|Object|String} collection The collection to convert. - * @returns {Array} Returns the new converted array. + * @category Objects + * @param {Mixed} a The value to compare. + * @param {Mixed} b The other value to compare. + * @param {Array} [stack=[]] Internally used to keep track of traversed objects + * to avoid circular references. + * @param {Object} thorough Internally used to indicate whether or not to perform + * a more thorough comparison of non-object values. + * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. * @example * - * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); - * // => [2, 3, 4] + * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; + * + * moe == clone; + * // => false + * + * _.isEqual(moe, clone); + * // => true */ - function toArray(collection) { - if (!collection) { - return []; + function isEqual(a, b, stack, thorough) { + // a strict comparison is necessary because `null == undefined` + if (a == null || b == null) { + return a === b; } - if (collection.toArray && toString.call(collection.toArray) == funcClass) { - return collection.toArray(); + // avoid slower checks on non-objects + thorough || (thorough = { 'value': null }); + if (thorough.value == null) { + // primitives passed from iframes use the primary document's native prototypes + thorough.value = !!(BoolProto.isEqual || NumberProto.isEqual || StringProto.isEqual); } - var length = collection.length; - if (length === length >>> 0) { - return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') - ? collection.split('') - : slice.call(collection); + if (objectTypes[typeof a] || objectTypes[typeof b] || thorough.value) { + // unwrap any LoDash wrapped values + if (a._chain) { + a = a._wrapped; + } + if (b._chain) { + b = b._wrapped; + } + // use custom `isEqual` method if available + if (a.isEqual && isFunction(a.isEqual)) { + thorough.value = null; + return a.isEqual(b); + } + if (b.isEqual && isFunction(b.isEqual)) { + thorough.value = null; + return b.isEqual(a); + } } - return values(collection); - } + // exit early for identical values + if (a === b) { + // treat `+0` vs. `-0` as not equal + return a !== 0 || (1 / a == 1 / b); + } + // compare [[Class]] names + var className = toString.call(a); + if (className != toString.call(b)) { + return false; + } + switch (className) { + 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; - /*--------------------------------------------------------------------------*/ + case numberClass: + // treat `NaN` vs. `NaN` as equal + return a != +a + ? b != +b + // but treat `+0` vs. `-0` as not equal + : (a == 0 ? (1 / a == 1 / b) : a == +b); - /** - * Produces a new array with all falsey values of `array` removed. The values - * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to compact. - * @returns {Array} Returns a new filtered array. - * @example - * - * _.compact([0, 1, false, 2, '', 3]); - * // => [1, 2, 3] - */ - function compact(array) { - var result = []; - if (!array) { - return result; + case regexpClass: + case stringClass: + // coerce regexes to strings (http://es5.github.com/#x15.10.6.4) + // treat string primitives and their corresponding object instances as equal + return a == b + ''; + } + // exit early, in older browsers, if `a` is array-like but not `b` + var isArr = arrayLikeClasses[className]; + if (noArgsClass && !isArr && (isArr = isArguments(a)) && !isArguments(b)) { + return false; + } + // exit for functions and DOM nodes + if (!isArr && (className != objectClass || (noNodeClass && ( + (typeof a.toString != 'function' && typeof (a + '') == 'string') || + (typeof b.toString != 'function' && typeof (b + '') == 'string'))))) { + return false; } - var index = -1, - length = array.length; - while (++index < length) { - if (array[index]) { - result.push(array[index]); + // 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.com/#x15.12.3) + stack || (stack = []); + var length = stack.length; + while (length--) { + if (stack[length] == a) { + return true; } } - return result; - } - /** - * Produces a new array of `array` values not present in the other arrays - * using strict equality for comparisons, i.e. `===`. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to process. - * @param {Array} [array1, array2, ...] Arrays to check. - * @returns {Array} Returns a new array of `array` values not present in the - * other arrays. - * @example - * - * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); - * // => [1, 3, 4] - */ - function difference(array) { - var result = []; - if (!array) { + var index = -1, + result = true, + size = 0; + + // add `a` to the stack of traversed objects + stack.push(a); + + // recursively compare objects and arrays (susceptible to call stack limits) + if (isArr) { + // compare lengths to determine if a deep comparison is necessary + size = a.length; + result = size == b.length; + + if (result) { + // deep compare the contents, ignoring non-numeric properties + while (size--) { + if (!(result = isEqual(a[size], b[size], stack, thorough))) { + break; + } + } + } return result; } - var index = -1, - length = array.length, - flattened = concat.apply(result, arguments), - contains = cachedContains(flattened, length); - while (++index < length) { - if (!contains(array[index])) { - result.push(array[index]); + var ctorA = a.constructor, + ctorB = b.constructor; + + // non `Object` object instances with different constructors are not equal + if (ctorA != ctorB && !( + isFunction(ctorA) && ctorA instanceof ctorA && + isFunction(ctorB) && ctorB instanceof ctorB + )) { + return false; + } + // deep compare objects + for (var prop in a) { + if (hasOwnProperty.call(a, prop)) { + // count the number of properties. + size++; + // deep compare each property value. + if (!(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) { + return false; + } } } - return result; + // ensure both objects have the same number of properties + for (prop in b) { + // The JS engine in Adobe products, like InDesign, has a bug that causes + // `!size--` to throw an error so it must be wrapped in parentheses. + // https://github.com/documentcloud/underscore/issues/355 + if (hasOwnProperty.call(b, prop) && !(size--)) { + // `size` will be `-1` if `b` has more properties than `a` + return false; + } + } + // handle JScript [[DontEnum]] bug + if (hasDontEnumBug) { + while (++index < 7) { + prop = shadowed[index]; + if (hasOwnProperty.call(a, prop) && + !(hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack, thorough))) { + return false; + } + } + } + return true; } /** - * Gets the first value of the `array`. Pass `n` to return the first `n` values - * of the `array`. + * Checks if `value` is a finite number. + * + * Note: This is not the same as native `isFinite`, which will return true for + * booleans and other values. See http://es5.github.com/#x15.1.2.5. * + * @deprecated * @static * @memberOf _ - * @alias head, take - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the first value or an array of the first `n` values - * of `array`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. * @example * - * _.first([5, 4, 3, 2, 1]); - * // => 5 + * _.isFinite(-101); + * // => true + * + * _.isFinite('10'); + * // => false + * + * _.isFinite(Infinity); + * // => false */ - function first(array, n, guard) { - if (array) { - return (n == null || guard) ? array[0] : slice.call(array, 0, n); - } + function isFinite(value) { + return nativeIsFinite(value) && toString.call(value) == numberClass; } /** - * Flattens a nested array (the nesting can be to any depth). If `shallow` is - * truthy, `array` will only be flattened a single level. + * Checks if `value` is the language type of Object. + * (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to compact. - * @param {Boolean} shallow A flag to indicate only flattening a single level. - * @returns {Array} Returns a new flattened array. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. * @example * - * _.flatten([1, [2], [3, [[4]]]]); - * // => [1, 2, 3, 4]; + * _.isObject({}); + * // => true * - * _.flatten([1, [2], [3, [[4]]]], true); - * // => [1, 2, 3, [[4]]]; + * _.isObject(1); + * // => false */ - function flatten(array, shallow) { - var result = []; - if (!array) { - return result; - } - var value, - index = -1, - length = array.length; - - while (++index < length) { - value = array[index]; - if (isArray(value)) { - push.apply(result, shallow ? value : flatten(value)); - } else { - result.push(value); - } - } - return result; + function isObject(value) { + // check if the value is the ECMAScript language type of Object + // http://es5.github.com/#x8 + // and avoid a V8 bug + // http://code.google.com/p/v8/issues/detail?id=2291 + return value && objectTypes[typeof value]; } /** - * Gets the index at which the first occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. If the `array` is already - * sorted, passing `true` for `isSorted` will run a faster binary search. + * Checks if `value` is `NaN`. + * + * Note: This is not the same as native `isNaN`, which will return true for + * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. * + * @deprecated * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {Mixed} value The value to search for. - * @param {Boolean|Number} [fromIndex=0] The index to start searching from or - * `true` to perform a binary search on a sorted `array`. - * @returns {Number} Returns the index of the matched value or `-1`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. * @example * - * _.indexOf([1, 2, 3, 1, 2, 3], 2); - * // => 1 + * _.isNaN(NaN); + * // => true * - * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 4 + * _.isNaN(new Number(NaN)); + * // => true * - * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); - * // => 2 - */ - function indexOf(array, value, fromIndex) { - if (!array) { - return -1; - } - var index = -1, - length = array.length; - - if (fromIndex) { - if (typeof fromIndex == 'number') { - index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; - } else { - index = sortedIndex(array, value); - return array[index] === value ? index : -1; - } - } - while (++index < length) { - if (array[index] === value) { - return index; - } - } - return -1; + * isNaN(undefined); + * // => true + * + * _.isNaN(undefined); + * // => false + */ + 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) + return toString.call(value) == numberClass && value != +value } /** - * Gets all but the last value of `array`. Pass `n` to exclude the last `n` - * values from the result. + * Checks if `value` is `null`. * + * @deprecated * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the last value or `n` values of `array`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. * @example * - * _.initial([3, 2, 1]); - * // => [3, 2] + * _.isNull(null); + * // => true + * + * _.isNull(undefined); + * // => false */ - function initial(array, n, guard) { - if (!array) { - return []; - } - return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + function isNull(value) { + return value === null; } /** - * Computes the intersection of all the passed-in arrays. + * Checks if `value` is a number. * * @static * @memberOf _ - * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique values, in order, that are - * present in **all** of the arrays. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. * @example * - * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); - * // => [1, 2] + * _.isNumber(8.4 * 5; + * // => true */ - function intersection(array) { - var result = []; - if (!array) { - return result; - } - var value, - index = -1, - length = array.length, - others = slice.call(arguments, 1), - cache = []; - - while (++index < length) { - value = array[index]; - if (indexOf(result, value) < 0 && - every(others, function(other, index) { - return (cache[index] || (cache[index] = cachedContains(other)))(value); - })) { - result.push(value); - } - } - return result; + function isNumber(value) { + return toString.call(value) == numberClass; } /** - * Gets the last value of the `array`. Pass `n` to return the lasy `n` values - * of the `array`. + * Checks if `value` is a regular expression. * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Mixed} Returns the last value or an array of the last `n` values - * of `array`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. * @example * - * _.last([3, 2, 1]); - * // => 1 + * _.isRegExp(/moe/); + * // => true */ - function last(array, n, guard) { - if (array) { - var length = array.length; - return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); - } + function isRegExp(value) { + return toString.call(value) == regexpClass; } /** - * Gets the index at which the last occurrence of `value` is found using - * strict equality for comparisons, i.e. `===`. + * Checks if `value` is a string. * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to search. - * @param {Mixed} value The value to search for. - * @param {Number} [fromIndex=array.length-1] The index to start searching from. - * @returns {Number} Returns the index of the matched value or `-1`. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. * @example * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); - * // => 4 - * - * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); - * // => 1 + * _.isString('moe'); + * // => true */ - function lastIndexOf(array, value, fromIndex) { - if (!array) { - return -1; - } - var index = array.length; - if (fromIndex && typeof fromIndex == 'number') { - index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; - } - while (index--) { - if (array[index] === value) { - return index; - } - } - return -1; + function isString(value) { + return toString.call(value) == stringClass; } /** - * Retrieves the maximum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to - * `thisArg` and invoked with 3 arguments; (value, index, array). + * Checks if `value` is `undefined`. * + * @deprecated * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the maximum value. + * @category Objects + * @param {Mixed} value The value to check. + * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. * @example * - * var stooges = [ - * { 'name': 'moe', 'age': 40 }, - * { 'name': 'larry', 'age': 50 }, - * { 'name': 'curly', 'age': 60 } - * ]; + * _.isUndefined(void 0); + * // => true + */ + function isUndefined(value) { + return value === undefined; + } + + /** + * Creates an array composed of the own enumerable property names of `object`. * - * _.max(stooges, function(stooge) { return stooge.age; }); - * // => { 'name': 'curly', 'age': 60 }; + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property names. + * @example + * + * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); + * // => ['one', 'two', 'three'] (order is not guaranteed) */ - function max(array, callback, thisArg) { - var computed = -Infinity, - result = computed; + var keys = !nativeKeys ? shimKeys : function(object) { + // avoid iterating over the `prototype` property + return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') + ? shimKeys(object) + : nativeKeys(object); + }; - if (!array) { - return result; - } - var current, - index = -1, - length = array.length; + /** + * 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. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The source object. + * @param {Object} [prop1, prop2, ...] The properties to pick. + * @returns {Object} Returns an object composed of the picked properties. + * @example + * + * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); + * // => { 'name': 'moe', 'age': 40 } + */ + function pick(object) { + var prop, + index = 0, + props = concat.apply(ArrayProto, arguments), + length = props.length, + result = {}; - if (!callback) { - while (++index < length) { - if (array[index] > result) { - result = array[index]; - } - } - return result; - } - if (thisArg) { - callback = iteratorBind(callback, thisArg); - } + // start `index` at `1` to skip `object` while (++index < length) { - current = callback(array[index], index, array); - if (current > computed) { - computed = current; - result = array[index]; + prop = props[index]; + if (prop in object) { + result[prop] = object[prop]; } } return result; } /** - * Retrieves the minimum value of an `array`. If `callback` is passed, - * it will be executed for each value in the `array` to generate the - * criterion by which the value is ranked. The `callback` is bound to `thisArg` - * and invoked with 3 arguments; (value, index, array). + * Gets the size of `value` by returning `value.length` if `value` is an + * array, string, or `arguments` object. If `value` is an object, size is + * determined by returning the number of own enumerable properties it has. * + * @deprecated * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function} [callback] The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Mixed} Returns the minimum value. + * @category Objects + * @param {Array|Object|String} value The value to inspect. + * @returns {Number} Returns `value.length` or number of own enumerable properties. * @example * - * _.min([10, 5, 100, 2, 1000]); + * _.size([1, 2]); * // => 2 + * + * _.size({ 'one': 1, 'two': 2, 'three': 3 }); + * // => 3 + * + * _.size('curly'); + * // => 5 */ - function min(array, callback, thisArg) { - var computed = Infinity, - result = computed; - - if (!array) { - return result; - } - var current, - index = -1, - length = array.length; - - if (!callback) { - while (++index < length) { - if (array[index] < result) { - result = array[index]; - } - } - return result; - } - if (thisArg) { - callback = iteratorBind(callback, thisArg); + function size(value) { + if (!value) { + return 0; } - while (++index < length) { - current = callback(array[index], index, array); - if (current < computed) { - computed = current; - result = array[index]; - } + var className = toString.call(value), + length = value.length; + + // return `value.length` for `arguments` objects, arrays, strings, and DOM + // query collections of libraries like jQuery and MooTools + // http://code.google.com/p/fbug/source/browse/branches/firebug1.9/content/firebug/chrome/reps.js?r=12614#653 + // http://trac.webkit.org/browser/trunk/Source/WebCore/inspector/InjectedScriptSource.js?rev=125186#L609 + if (arrayLikeClasses[className] || (noArgsClass && isArguments(value)) || + (className == objectClass && length > -1 && length === length >>> 0 && isFunction(value.splice))) { + return length; } - return result; + return keys(value).length; } /** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to but not including `stop`. This method is a port of Python's - * `range()` function. See http://docs.python.org/library/functions.html#range. + * Creates an array composed of the own enumerable property values of `object`. * * @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 descrement by. - * @returns {Array} Returns a new range array. + * @category Objects + * @param {Object} object The object to inspect. + * @returns {Array} Returns a new array of property values. * @example * - * _.range(10); - * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * _.values({ 'one': 1, 'two': 2, 'three': 3 }); + * // => [1, 2, 3] + */ + var values = createIterator({ + 'args': 'object', + 'init': '[]', + 'inLoop': 'result.push(value)' + }); + + /*--------------------------------------------------------------------------*/ + + /** + * Checks if a given `target` element is present in a `collection` using strict + * equality for comparisons, i.e. `===`. * - * _.range(1, 11); - * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * @static + * @memberOf _ + * @alias include + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Mixed} target The value to check for. + * @returns {Boolean} Returns `true` if the `target` element is found, else `false`. + * @example * - * _.range(0, 30, 5); - * // => [0, 5, 10, 15, 20, 25] + * _.contains([1, 2, 3], 3); + * // => true * - * _.range(0, -10, -1); - * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * _.contains({ 'name': 'moe', 'age': 40 }, 'moe'); + * // => true * - * _.range(0); - * // => [] + * _.contains('curly', 'ur'); + * // => true */ - function range(start, end, step) { - step || (step = 1); - if (end == null) { - end = start || 0; - start = 0; - } - // use `Array(length)` so V8 will avoid the slower "dictionary" mode - // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s - var index = -1, - length = Math.max(0, Math.ceil((end - start) / step)), - result = Array(length); - - while (++index < length) { - result[index] = start; - start += step; - } - return result; - } + var contains = createIterator({ + 'args': 'collection, target', + 'init': 'false', + 'noCharByIndex': false, + 'beforeLoop': { + 'array': 'if (toString.call(iteratee) == stringClass) return collection.indexOf(target) > -1' + }, + 'inLoop': 'if (value === target) return true' + }); /** - * The opposite of `_.initial`, this method gets all but the first value of - * `array`. Pass `n` to exclude the first `n` values from the result. + * Creates an object composed of keys returned from running each element of + * `collection` through a `callback`. The corresponding value of each key is + * the number of times the key was returned by `callback`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). + * The `callback` argument may also be the name of a property to count by (e.g. 'length'). * * @static * @memberOf _ - * @alias tail - * @category Arrays - * @param {Array} array The array to query. - * @param {Number} [n] The number of elements to return. - * @param {Object} [guard] Internally used to allow this method to work with - * others like `_.map` without using their callback `index` argument for `n`. - * @returns {Array} Returns all but the first value or `n` values of `array`. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to count by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the composed aggregate object. * @example * - * _.rest([3, 2, 1]); - * // => [2, 1] + * _.countBy([4.3, 6.1, 6.4], function(num) { return Math.floor(num); }); + * // => { '4': 1, '6': 2 } + * + * _.countBy([4.3, 6.1, 6.4], function(num) { return this.floor(num); }, Math); + * // => { '4': 1, '6': 2 } + * + * _.countBy(['one', 'two', 'three'], 'length'); + * // => { '3': 2, '5': 1 } */ - function rest(array, n, guard) { - if (!array) { - return []; - } - return slice.call(array, (n == null || guard) ? 1 : n); - } + var countBy = createIterator(baseIteratorOptions, countByIteratorOptions); /** - * Produces a new array of shuffled `array` values, using a version of the - * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. + * Checks if the `callback` returns a truthy value for **all** elements of a + * `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, index|key, collection). * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to shuffle. - * @returns {Array} Returns a new shuffled array. + * @alias all + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if all elements pass the callback check, else `false`. * @example * - * _.shuffle([1, 2, 3, 4, 5, 6]); - * // => [4, 1, 6, 3, 5, 2] + * _.every([true, 1, null, 'yes'], Boolean); + * // => false */ - function shuffle(array) { - if (!array) { - return []; - } - var rand, - index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - rand = Math.floor(Math.random() * (index + 1)); - result[index] = result[rand]; - result[rand] = array[index]; - } - return result; - } + var every = createIterator(baseIteratorOptions, everyIteratorOptions); /** - * Uses a binary search to determine the smallest index at which the `value` - * should be inserted into `array` in order to maintain the sort order of the - * sorted `array`. If `callback` is passed, it will be executed for `value` and - * each element in `array` to compute their sort ranking. The `callback` is - * bound to `thisArg` and invoked with 1 argument; (value). + * Examines each element in a `collection`, returning an array of all elements + * the `callback` returns truthy for. The `callback` is bound to `thisArg` and + * invoked with 3 arguments; (value, index|key, collection). * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Mixed} value The value to evaluate. + * @alias select + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. * @param {Function} [callback=identity] The function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Number} Returns the index at which the value should be inserted - * into `array`. + * @returns {Array} Returns a new array of elements that passed callback check. * @example * - * _.sortedIndex([20, 30, 40], 35); - * // => 2 - * - * var dict = { - * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } - * }; - * - * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { - * return dict.wordToNumber[word]; - * }); - * // => 2 - * - * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { - * return this.wordToNumber[word]; - * }, dict); - * // => 2 + * var evens = _.filter([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [2, 4, 6] */ - function sortedIndex(array, value, callback, thisArg) { - if (!array) { - return 0; - } - var mid, - low = 0, - high = array.length; - - if (callback) { - if (thisArg) { - callback = bind(callback, thisArg); - } - value = callback(value); - while (low < high) { - mid = (low + high) >>> 1; - callback(array[mid]) < value ? low = mid + 1 : high = mid; - } - } else { - while (low < high) { - mid = (low + high) >>> 1; - array[mid] < value ? low = mid + 1 : high = mid; - } - } - return low; - } + var filter = createIterator(baseIteratorOptions, filterIteratorOptions); /** - * Computes the union of the passed-in arrays. + * Examines each element in a `collection`, returning the first one the `callback` + * returns truthy for. The function returns as soon as it finds an acceptable + * element, and does not iterate over the entire `collection`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). * * @static * @memberOf _ - * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of unique values, in order, that are - * present in one or more of the arrays. + * @alias detect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the element that passed the callback check, else `undefined`. * @example * - * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); - * // => [1, 2, 3, 101, 10] + * var even = _.find([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => 2 */ - function union() { - var index = -1, - result = [], - flattened = concat.apply(result, arguments), - length = flattened.length; - - while (++index < length) { - if (indexOf(result, flattened[index]) < 0) { - result.push(flattened[index]); - } - } - return result; - } + var find = createIterator(baseIteratorOptions, forEachIteratorOptions, { + 'init': '', + 'inLoop': 'if (callback(value, index, collection)) return value' + }); /** - * Produces a duplicate-value-free version of the `array` using strict equality - * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` - * for `isSorted` will run a faster algorithm. If `callback` is passed, - * each value of `array` is passed through a transformation `callback` before - * uniqueness is computed. The `callback` is bound to `thisArg` and invoked - * with 3 arguments; (value, index, array). + * Iterates over a `collection`, executing the `callback` for each element in + * the `collection`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, index|key, collection). Callbacks may exit iteration + * early by explicitly returning `false`. * * @static * @memberOf _ - * @alias unique - * @category Arrays - * @param {Array} array The array to process. - * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. - * @param {Function} [callback=identity] The function called per iteration. + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a duplicate-value-free array. + * @returns {Array|Object} Returns `collection`. * @example * - * _.uniq([1, 2, 1, 3, 1]); - * // => [1, 2, 3] - * - * _.uniq([1, 1, 2, 2, 3], true); - * // => [1, 2, 3] - * - * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); - * // => [1, 2, 3] + * _([1, 2, 3]).forEach(alert).join(','); + * // => alerts each number and returns '1,2,3' * - * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); - * // => [1, 2, 3] + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); + * // => alerts each number (order is not guaranteed) */ - function uniq(array, isSorted, callback, thisArg) { - var result = []; - if (!array) { - return result; - } - var computed, - index = -1, - length = array.length, - seen = []; - - // juggle arguments - if (typeof isSorted == 'function') { - thisArg = callback; - callback = isSorted; - isSorted = false; - } - if (!callback) { - callback = identity; - } else if (thisArg) { - callback = iteratorBind(callback, thisArg); - } - while (++index < length) { - computed = callback(array[index], index, array); - if (isSorted - ? !index || seen[seen.length - 1] !== computed - : indexOf(seen, computed) < 0 - ) { - seen.push(computed); - result.push(array[index]); - } - } - return result; - } + var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); /** - * Produces a new array with all occurrences of the passed values removed using - * strict equality for comparisons, i.e. `===`. + * Creates an object composed of keys returned from running each element of + * `collection` through a `callback`. The corresponding value of each key is an + * array of elements passed to `callback` that returned the key. The `callback` + * is bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). + * The `callback` argument may also be the name of a property to count by (e.g. 'length'). * * @static * @memberOf _ - * @category Arrays - * @param {Array} array The array to filter. - * @param {Mixed} [value1, value2, ...] Values to remove. - * @returns {Array} Returns a new filtered array. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns the composed aggregate object. * @example * - * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); - * // => [2, 3, 4] + * _.groupBy([4.2, 6.1, 6.4], function(num) { return Math.floor(num); }); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy([4.2, 6.1, 6.4], function(num) { return this.floor(num); }, Math); + * // => { '4': [4.2], '6': [6.1, 6.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } */ - function without(array) { - var result = []; - if (!array) { - return result; - } - var index = -1, - length = array.length, - contains = cachedContains(arguments, 1, 20); - - while (++index < length) { - if (!contains(array[index])) { - result.push(array[index]); - } - } - return result; - } + var groupBy = createIterator(baseIteratorOptions, countByIteratorOptions, { + 'inLoop': + 'prop = callback(value, index, collection);\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value)' + }); /** - * Merges the elements of each array at their corresponding indexes. Useful for - * separate data sources that are coordinated through matching array indexes. - * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix - * in a similar fashion. + * Invokes the method named by `methodName` on each element in the `collection`. + * Additional arguments will be passed to each invoked method. If `methodName` + * is a function it will be invoked for, and `this` bound to, each element + * in the `collection`. * * @static * @memberOf _ - * @category Arrays - * @param {Array} [array1, array2, ...] Arrays to process. - * @returns {Array} Returns a new array of merged arrays. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} methodName The name of the method to invoke or + * the function invoked per iteration. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with. + * @returns {Array} Returns a new array of values returned from each invoked method. * @example * - * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); - * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + * _.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); + * // => [[1, 5, 7], [1, 2, 3]] + * + * _.invoke([123, 456], String.prototype.split, ''); + * // => [['1', '2', '3'], ['4', '5', '6']] */ - function zip(array) { - if (!array) { - return []; - } - var index = -1, - length = max(pluck(arguments, 'length')), - result = Array(length); - - while (++index < length) { - result[index] = pluck(arguments, index); + var invoke = createIterator(mapIteratorOptions, { + 'args': 'collection, methodName', + 'top': + 'var args = slice.call(arguments, 2),\n' + + ' isFunc = typeof methodName == \'function\'', + 'inLoop': { + 'array': + 'result[index] = (isFunc ? methodName : value[methodName]).apply(value, args)', + 'object': + 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + + '((isFunc ? methodName : value[methodName]).apply(value, args))' } - return result; - } + }); /** - * Merges an array of `keys` and an array of `values` into a single object. + * Creates a new array of values by running each element in the `collection` + * through a `callback`. The `callback` is bound to `thisArg` and invoked with + * 3 arguments; (value, index|key, collection). * * @static * @memberOf _ - * @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. + * @alias collect + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of elements returned by the callback. * @example * - * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); - * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + * _.map([1, 2, 3], function(num) { return num * 3; }); + * // => [3, 6, 9] + * + * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); + * // => [3, 6, 9] (order is not guaranteed) */ - function zipObject(keys, values) { - if (!keys) { - return {}; - } - var index = -1, - length = keys.length, - result = {}; - - values || (values = []); - while (++index < length) { - result[keys[index]] = values[index]; - } - return result; - } - - /*--------------------------------------------------------------------------*/ + var map = createIterator(baseIteratorOptions, mapIteratorOptions); /** - * Creates a new function that is restricted to executing only after it is - * called `n` times. + * Merges enumerable properties of the source object(s) into the `destination` + * object. Subsequent sources will overwrite propery assignments of previous + * sources. * * @static * @memberOf _ - * @category Functions - * @param {Number} n The number of times the function must be called before - * it is executed. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. + * @category Objects + * @param {Object} object The destination object. + * @param {Object} [source1, source2, ...] The source objects. + * @param {Object} [indicator] Internally used to indicate that the `stack` + * argument is an array of traversed objects instead of another source object. + * @param {Array} [stack=[]] Internally used to keep track of traversed objects + * to avoid circular references. + * @returns {Object} Returns the destination object. * @example * - * var renderNotes = _.after(notes.length, render); - * _.forEach(notes, function(note) { - * note.asyncSave({ 'success': renderNotes }); - * }); - * // `renderNotes` is run once, after all notes have saved + * var stooges = [ + * { 'name': 'moe' }, + * { 'name': 'larry' } + * ]; + * + * var ages = [ + * { 'age': 40 }, + * { 'age': 50 } + * ]; + * + * _.merge(stooges, ages); + * // => [{ 'name': 'moe', 'age': 40 }, { 'name': 'larry', 'age': 50 }] */ - function after(n, func) { - if (n < 1) { - return func(); - } - return function() { - if (--n < 1) { - return func.apply(this, arguments); - } - }; - } + var merge = createIterator(extendIteratorOptions, { + 'args': 'object, source, indicator, stack', + 'top': + 'var destValue, found, isArr, stackLength, recursive = indicator == isPlainObject;\n' + + 'if (!recursive) stack = [];\n' + + 'for (var argsIndex = 1, argsLength = recursive ? 2 : arguments.length; argsIndex < argsLength; argsIndex++) {\n' + + ' if (iteratee = arguments[argsIndex]) {', + 'inLoop': + 'if (value && ((isArr = isArray(value)) || isPlainObject(value))) {\n' + + ' found = false; stackLength = stack.length;\n' + + ' while (stackLength--) {\n' + + ' if (found = stack[stackLength].source == value) break\n' + + ' }\n' + + ' if (found) {\n' + + ' result[index] = stack[stackLength].value\n' + + ' } else {\n' + + ' destValue = (destValue = result[index]) && isArr\n' + + ' ? (isArray(destValue) ? destValue : [])\n' + + ' : (isPlainObject(destValue) ? destValue : {});\n' + + ' stack.push({ value: destValue, source: value });\n' + + ' result[index] = callee(destValue, value, isPlainObject, stack)\n' + + ' }\n' + + '} else if (value != null) {\n' + + ' result[index] = value\n' + + '}' + }); /** - * Creates a new function that, when called, invokes `func` with the `this` - * binding of `thisArg` and prepends any additional `bind` arguments to those - * passed to the bound function. Lazy defined methods may be bound by passing - * the object they are bound to as `func` and the method name as `thisArg`. + * Retrieves the value of a specified property from all elements in + * the `collection`. * * @static * @memberOf _ - * @category Functions - * @param {Function|Object} func The function to bind or the object the method belongs to. - * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. - * @returns {Function} Returns the new bound function. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {String} property The property to pluck. + * @returns {Array} Returns a new array of property values. * @example * - * // basic bind - * var func = function(greeting) { - * return greeting + ' ' + this.name; - * }; - * - * func = _.bind(func, { 'name': 'moe' }, 'hi'); - * func(); - * // => 'hi moe' - * - * // lazy bind - * var object = { - * 'name': 'moe', - * 'greet': function(greeting) { - * return greeting + ' ' + this.name; - * } - * }; - * - * var func = _.bind(object, 'greet', 'hi'); - * func(); - * // => 'hi moe' - * - * object.greet = function(greeting) { - * return greeting + ', ' + this.name + '!'; - * }; + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; * - * func(); - * // => 'hi, moe!' + * _.pluck(stooges, 'name'); + * // => ['moe', 'larry', 'curly'] */ - function bind(func, thisArg) { - var methodName, - isFunc = toString.call(func) == funcClass; - - // juggle arguments - if (!isFunc) { - methodName = thisArg; - thisArg = func; - } - // use `Function#bind` if it exists and is fast - // (in V8 `Function#bind` is slower except when partially applied) - else if (isBindFast || (nativeBind && arguments.length > 2)) { - return nativeBind.call.apply(nativeBind, arguments); + var pluck = createIterator(mapIteratorOptions, { + 'args': 'collection, property', + 'inLoop': { + 'array': 'result[index] = value[property]', + 'object': 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '(value[property])' } - - var partialArgs = slice.call(arguments, 2); - - function bound() { - // `Function#bind` spec - // http://es5.github.com/#x15.3.4.5 - var args = arguments, - thisBinding = thisArg; - - if (!isFunc) { - func = thisArg[methodName]; - } - if (partialArgs.length) { - args = args.length - ? concat.apply(partialArgs, args) - : partialArgs; - } - if (this instanceof bound) { - // get `func` instance if `bound` is invoked in a `new` expression - noop.prototype = func.prototype; - thisBinding = new noop; - - // mimic the constructor's `return` behavior - // http://es5.github.com/#x13.2.2 - var result = func.apply(thisBinding, args); - return result && objectTypes[typeof result] - ? result - : thisBinding - } - return func.apply(thisBinding, args); - } - return bound; - } + }); /** - * Binds methods on `object` to `object`, overwriting the existing method. - * If no method names are provided, all the function properties of `object` - * will be bound. + * Boils down a `collection` to a single value. The initial state of the + * reduction is `accumulator` and each successive step of it should be returned + * by the `callback`. The `callback` is bound to `thisArg` and invoked with 4 + * arguments; for arrays they are (accumulator, value, index|key, collection). * * @static * @memberOf _ - * @category Functions - * @param {Object} object The object to bind and assign the bound methods to. - * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. - * @returns {Object} Returns the `object`. + * @alias foldl, inject + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. * @example * - * var buttonView = { - * 'label': 'lodash', - * 'onClick': function() { alert('clicked: ' + this.label); } - * }; - * - * _.bindAll(buttonView); - * jQuery('#lodash_button').on('click', buttonView.onClick); - * // => When the button is clicked, `this.label` will have the correct value + * var sum = _.reduce([1, 2, 3], function(memo, num) { return memo + num; }); + * // => 6 */ - var bindAll = createIterator({ - 'useHas': false, - 'useStrict': false, - 'args': 'object', - 'init': 'object', + var reduce = createIterator({ + 'args': 'collection, callback, accumulator, thisArg', + 'init': 'accumulator', 'top': - 'var funcs = arguments,\n' + - ' length = funcs.length;\n' + - 'if (length > 1) {\n' + - ' for (var index = 1; index < length; index++)\n' + - ' result[funcs[index]] = bind(result[funcs[index]], result);\n' + - ' return result\n' + - '}', - 'inLoop': - 'if (toString.call(result[index]) == funcClass)' + - ' result[index] = bind(result[index], result)' + 'var noaccum = arguments.length < 3;\n' + + 'if (thisArg) callback = iteratorBind(callback, thisArg)', + 'beforeLoop': { + 'array': 'if (noaccum) result = collection[++index]' + }, + 'inLoop': { + 'array': + 'result = callback(result, value, index, collection)', + 'object': + 'result = noaccum\n' + + ' ? (noaccum = false, value)\n' + + ' : callback(result, value, index, collection)' + } }); /** - * Creates a new function that is the composition of the passed functions, - * where each function consumes the return value of the function that follows. - * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * The right-associative version of `_.reduce`. * * @static * @memberOf _ - * @category Functions - * @param {Function} [func1, func2, ...] Functions to compose. - * @returns {Function} Returns the new composed function. + * @alias foldr + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} callback The function called per iteration. + * @param {Mixed} [accumulator] Initial value of the accumulator. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the accumulated value. * @example * - * var greet = function(name) { return 'hi: ' + name; }; - * var exclaim = function(statement) { return statement + '!'; }; - * var welcome = _.compose(exclaim, greet); - * welcome('moe'); - * // => 'hi: moe!' + * var list = [[0, 1], [2, 3], [4, 5]]; + * var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []); + * // => [4, 5, 2, 3, 0, 1] */ - function compose() { - var funcs = arguments; - return function() { - var args = arguments, - length = funcs.length; + function reduceRight(collection, callback, accumulator, thisArg) { + if (!collection) { + return accumulator; + } - while (length--) { - args = [funcs[length].apply(this, args)]; - } - return args[0]; - }; - } + var length = collection.length, + noaccum = arguments.length < 3; - /** - * Creates a new function that will delay the execution of `func` until after - * `wait` milliseconds have elapsed since the last time it was invoked. Pass - * `true` for `immediate` to cause debounce to invoke `func` on the leading, - * instead of the trailing, edge of the `wait` timeout. Subsequent calls to - * the debounced function will return the result of the last `func` call. - * - * @static - * @memberOf _ - * @category Functions - * @param {Function} func The function to debounce. - * @param {Number} wait The number of milliseconds to delay. - * @param {Boolean} immediate A flag to indicate execution is on the leading - * edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * var lazyLayout = _.debounce(calculateLayout, 300); - * jQuery(window).on('resize', lazyLayout); - */ - function debounce(func, wait, immediate) { - var args, - result, - thisArg, - timeoutId; + if(thisArg) { + callback = iteratorBind(callback, thisArg); + } + // Opera 10.53-10.60 JITted `length >>> 0` returns the wrong value for negative numbers + if (length > -1 && length === length >>> 0) { + var iteratee = noCharByIndex && toString.call(collection) == stringClass + ? collection.split('') + : collection; - function delayed() { - timeoutId = null; - if (!immediate) { - func.apply(thisArg, args); + if (length && noaccum) { + accumulator = iteratee[--length]; + } + while (length--) { + accumulator = callback(accumulator, iteratee[length], length, collection); } + return accumulator; } - return function() { - var isImmediate = immediate && !timeoutId; - args = arguments; - thisArg = this; - - clearTimeout(timeoutId); - timeoutId = setTimeout(delayed, wait); + var prop, + props = keys(collection); - if (isImmediate) { - result = func.apply(thisArg, args); - } - return result; - }; + length = props.length; + if (length && noaccum) { + accumulator = collection[props[--length]]; + } + while (length--) { + prop = props[length]; + accumulator = callback(accumulator, collection[prop], prop, collection); + } + return accumulator; } /** - * Executes the `func` function after `wait` milliseconds. Additional arguments - * are passed to `func` when it is invoked. + * The opposite of `_.filter`, this method returns the values of a + * `collection` that `callback` does **not** return truthy for. * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to delay. - * @param {Number} wait The number of milliseconds to delay execution. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of elements that did **not** pass the callback check. * @example * - * var log = _.bind(console.log, console); - * _.delay(log, 1000, 'logged later'); - * // => 'logged later' (Appears after one second.) + * var odds = _.reject([1, 2, 3, 4, 5, 6], function(num) { return num % 2 == 0; }); + * // => [1, 3, 5] */ - function delay(func, wait) { - var args = slice.call(arguments, 2); - return setTimeout(function() { return func.apply(undefined, args); }, wait); - } + var reject = createIterator(baseIteratorOptions, filterIteratorOptions, { + 'inLoop': '!' + filterIteratorOptions.inLoop + }); /** - * Defers executing the `func` function until the current call stack has cleared. - * Additional arguments are passed to `func` when it is invoked. + * Checks if the `callback` returns a truthy value for **any** element of a + * `collection`. The function returns as soon as it finds passing value, and + * does not iterate over the entire `collection`. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index|key, collection). * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to defer. - * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. - * @returns {Number} Returns the `setTimeout` timeout id. + * @alias any + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Boolean} Returns `true` if any element passes the callback check, else `false`. * @example * - * _.defer(function() { alert('deferred'); }); - * // returns from the function before `alert` is called + * _.some([null, 0, 'yes', false]); + * // => true */ - function defer(func) { - var args = slice.call(arguments, 1); - return setTimeout(function() { return func.apply(undefined, args); }, 1); - } + var some = createIterator(baseIteratorOptions, everyIteratorOptions, { + 'init': 'false', + 'inLoop': everyIteratorOptions.inLoop.replace('!', '') + }); /** - * Creates a new function that memoizes the result of `func`. If `resolver` is - * passed, it will be used to determine the cache key for storing the result - * based on the arguments passed to the memoized function. By default, the first - * argument passed to the memoized function is used as the cache key. + * Creates a new sorted array, sorted in ascending order by the results of + * running each element of `collection` through a `callback`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; (value, index|key, collection). + * The `callback` argument may also be the name of a property to sort by (e.g. 'length'). * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] A function used to resolve the cache key. - * @returns {Function} Returns the new memoizing function. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted elements. * @example * - * var fibonacci = _.memoize(function(n) { - * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); - * }); - */ - function memoize(func, resolver) { - var cache = {}; - return function() { - var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; - return hasOwnProperty.call(cache, prop) - ? cache[prop] - : (cache[prop] = func.apply(this, arguments)); - }; - } + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + var sortBy = createIterator(baseIteratorOptions, countByIteratorOptions, mapIteratorOptions, { + 'inLoop': { + 'array': + 'result[index] = {\n' + + ' criteria: callback(value, index, collection),\n' + + ' index: index,\n' + + ' value: value\n' + + '}', + 'object': + 'result' + (isKeysFast ? '[ownIndex] = ' : '.push') + '({\n' + + ' criteria: callback(value, index, collection),\n' + + ' index: index,\n' + + ' value: value\n' + + '})' + }, + 'bottom': + 'result.sort(compareAscending);\n' + + 'length = result.length;\n' + + 'while (length--) {\n' + + ' result[length] = result[length].value\n' + + '}' + }); /** - * Creates a new function that is restricted to one execution. Repeat calls to - * the function will return the value of the first call. + * Converts the `collection`, into an array. Useful for converting the + * `arguments` object. * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. + * @category Collections + * @param {Array|Object|String} collection The collection to convert. + * @returns {Array} Returns the new converted array. * @example * - * var initialize = _.once(createApplication); - * initialize(); - * initialize(); - * // Application is only created once. + * (function() { return _.toArray(arguments).slice(1); })(1, 2, 3, 4); + * // => [2, 3, 4] */ - function once(func) { - var result, - ran = false; - - return function() { - if (ran) { - return result; - } - ran = true; - result = func.apply(this, arguments); - return result; - }; + function toArray(collection) { + if (!collection) { + return []; + } + if (collection.toArray && isFunction(collection.toArray)) { + return collection.toArray(); + } + var length = collection.length; + if (length > -1 && length === length >>> 0) { + return (noArraySliceOnStrings ? toString.call(collection) == stringClass : typeof collection == 'string') + ? collection.split('') + : slice.call(collection); + } + return values(collection); } /** - * Creates a new function that, when called, invokes `func` with any additional - * `partial` arguments prepended to those passed to the partially applied - * function. This method is similar `bind`, except it does **not** alter the - * `this` binding. + * Examines each element in a `collection`, returning an array of all elements + * that contain the given `properties`. * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to partially apply arguments to. - * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. - * @returns {Function} Returns the new partially applied function. + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Object} properties The object of properties/values to filter by. + * @returns {Array} Returns a new array of elements that contain the given `properties`. * @example * - * var greet = function(greeting, name) { return greeting + ': ' + name; }; - * var hi = _.partial(greet, 'hi'); - * hi('moe'); - * // => 'hi: moe' + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; + * + * _.where(stooges, { 'age': 40 }); + * // => [{ 'name': 'moe', 'age': 40 }] */ - function partial(func) { - var args = slice.call(arguments, 1), - argsLength = args.length; - - return function() { - var result, - others = arguments; + var where = createIterator(filterIteratorOptions, { + 'args': 'collection, properties', + 'top': + 'var pass, prop, propIndex, props = [];\n' + + 'forIn(properties, function(value, prop) { props.push(prop) });\n' + + 'var propsLength = props.length', + 'inLoop': + 'for (pass = true, propIndex = 0; propIndex < propsLength; propIndex++) {\n' + + ' prop = props[propIndex];\n' + + ' if (!(pass = value[prop] === properties[prop])) break\n' + + '}\n' + + 'if (pass) result.push(value)' + }); - if (others.length) { - args.length = argsLength; - push.apply(args, others); - } - result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); - args.length = argsLength; - return result; - }; - } + /*--------------------------------------------------------------------------*/ /** - * Creates a new function that, when executed, will only call the `func` - * function at most once per every `wait` milliseconds. If the throttled - * function is invoked more than once during the `wait` timeout, `func` will - * also be called on the trailing edge of the timeout. Subsequent calls to the - * throttled function will return the result of the last `func` call. + * Creates a new array with all falsey values of `array` removed. The values + * `false`, `null`, `0`, `""`, `undefined` and `NaN` are all falsey. * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to throttle. - * @param {Number} wait The number of milliseconds to throttle executions to. - * @returns {Function} Returns the new throttled function. + * @category Arrays + * @param {Array} array The array to compact. + * @returns {Array} Returns a new filtered array. * @example * - * var throttled = _.throttle(updatePosition, 100); - * jQuery(window).on('scroll', throttled); + * _.compact([0, 1, false, 2, '', 3]); + * // => [1, 2, 3] */ - function throttle(func, wait) { - var args, - result, - thisArg, - timeoutId, - lastCalled = 0; - - function trailingCall() { - lastCalled = new Date; - timeoutId = null; - func.apply(thisArg, args); + function compact(array) { + var result = []; + if (!array) { + return result; } + var index = -1, + length = array.length; - return function() { - var now = new Date, - remain = wait - (now - lastCalled); - - args = arguments; - thisArg = this; - - if (remain <= 0) { - lastCalled = now; - result = func.apply(thisArg, args); - } - else if (!timeoutId) { - timeoutId = setTimeout(trailingCall, remain); + while (++index < length) { + if (array[index]) { + result.push(array[index]); } - return result; - }; + } + return result; } /** - * Create a new function that passes the `func` function to the `wrapper` - * function as its first argument. Additional arguments are appended to those - * passed to the `wrapper` function. + * Creates a new array of `array` elements not present in the other arrays + * using strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ - * @category Functions - * @param {Function} func The function to wrap. - * @param {Function} wrapper The wrapper function. - * @param {Mixed} [arg1, arg2, ...] Arguments to append to those passed to the wrapper. - * @returns {Function} Returns the new function. + * @category Arrays + * @param {Array} array The array to process. + * @param {Array} [array1, array2, ...] Arrays to check. + * @returns {Array} Returns a new array of `array` elements not present in the + * other arrays. * @example * - * var hello = function(name) { return 'hello: ' + name; }; - * hello = _.wrap(hello, function(func) { - * return 'before, ' + func('moe') + ', after'; - * }); - * hello(); - * // => 'before, hello: moe, after' + * _.difference([1, 2, 3, 4, 5], [5, 2, 10]); + * // => [1, 3, 4] */ - function wrap(func, wrapper) { - return function() { - var args = [func]; - if (arguments.length) { - push.apply(args, arguments); + function difference(array) { + var result = []; + if (!array) { + return result; + } + var index = -1, + length = array.length, + flattened = concat.apply(result, arguments), + contains = cachedContains(flattened, length); + + while (++index < length) { + if (!contains(array[index])) { + result.push(array[index]); } - return wrapper.apply(this, args); - }; + } + return result; } - /*--------------------------------------------------------------------------*/ - /** - * Create a shallow clone of the `value`. Any nested objects or arrays will be - * assigned by reference and not cloned. + * Gets the first element of the `array`. Pass `n` to return the first `n` + * elements of the `array`. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to clone. - * @returns {Mixed} Returns the cloned `value`. + * @alias head, take + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the first element or an array of the first `n` + * elements of `array`. * @example * - * _.clone({ 'name': 'moe' }); - * // => { 'name': 'moe' }; + * _.first([5, 4, 3, 2, 1]); + * // => 5 */ - function clone(value) { - return value && objectTypes[typeof value] - ? (isArray(value) ? value.slice() : extend({}, value)) - : value; + function first(array, n, guard) { + if (array) { + return (n == null || guard) ? array[0] : slice.call(array, 0, n); + } } /** - * Assigns missing properties on `object` with default values from the defaults - * objects. Once a property is set, additional defaults of the same property - * will be ignored. + * Flattens a nested array (the nesting can be to any depth). If `shallow` is + * truthy, `array` will only be flattened a single level. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to populate. - * @param {Object} [defaults1, defaults2, ...] The defaults objects to apply to `object`. - * @returns {Object} Returns `object`. + * @category Arrays + * @param {Array} array The array to compact. + * @param {Boolean} shallow A flag to indicate only flattening a single level. + * @returns {Array} Returns a new flattened array. * @example * - * var iceCream = { 'flavor': 'chocolate' }; - * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' }); - * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' } + * _.flatten([1, [2], [3, [[4]]]]); + * // => [1, 2, 3, 4]; + * + * _.flatten([1, [2], [3, [[4]]]], true); + * // => [1, 2, 3, [[4]]]; */ - var defaults = createIterator(extendIteratorOptions, { - 'inLoop': 'if (result[index] == null) ' + extendIteratorOptions.inLoop - }); + function flatten(array, shallow) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length; + + while (++index < length) { + value = array[index]; + if (isArray(value)) { + push.apply(result, shallow ? value : flatten(value)); + } else { + result.push(value); + } + } + return result; + } /** - * Copies enumerable properties from the source objects to the `destination` object. - * Subsequent sources will overwrite propery assignments of previous sources. + * Gets the index at which the first occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. If the `array` is already + * sorted, passing `true` for `isSorted` will run a faster binary search. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The destination object. - * @param {Object} [source1, source2, ...] The source objects. - * @returns {Object} Returns the destination object. + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Boolean|Number} [fromIndex=0] The index to start searching from or + * `true` to perform a binary search on a sorted `array`. + * @returns {Number} Returns the index of the matched value or `-1`. * @example * - * _.extend({ 'name': 'moe' }, { 'age': 40 }); - * // => { 'name': 'moe', 'age': 40 } + * _.indexOf([1, 2, 3, 1, 2, 3], 2); + * // => 1 + * + * _.indexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 4 + * + * _.indexOf([1, 1, 2, 2, 3, 3], 2, true); + * // => 2 */ - var extend = createIterator(extendIteratorOptions); + function indexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = -1, + length = array.length; + + if (fromIndex) { + if (typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, length + fromIndex) : fromIndex) - 1; + } else { + index = sortedIndex(array, value); + return array[index] === value ? index : -1; + } + } + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } /** - * Iterates over `object`'s own and inherited enumerable properties, executing - * the `callback` for each property. The `callback` is bound to `thisArg` and - * invoked with 3 arguments; (value, key, object). + * Gets all but the last element of `array`. Pass `n` to exclude the last `n` + * elements from the result. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns the `object`. + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the last element or `n` elements of `array`. * @example * - * function Dog(name) { - * this.name = name; - * } - * - * Dog.prototype.bark = function() { - * alert('Woof, woof!'); - * }; - * - * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); - * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) + * _.initial([3, 2, 1]); + * // => [3, 2] */ - var forIn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions, { - 'useHas': false - }); + function initial(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, 0, -((n == null || guard) ? 1 : n)); + } /** - * Iterates over `object`'s own enumerable properties, executing the `callback` - * for each property. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, key, object). + * Computes the intersection of all the passed-in arrays using strict equality + * for comparisons, i.e. `===`. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to iterate over. - * @param {Function} callback The function called per iteration. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns the `object`. + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique elements, in order, that are + * present in **all** of the arrays. * @example * - * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); - * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) + * _.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2] */ - var forOwn = createIterator(baseIteratorOptions, forEachIteratorOptions, forOwnIteratorOptions); + function intersection(array) { + var result = []; + if (!array) { + return result; + } + var value, + index = -1, + length = array.length, + others = slice.call(arguments, 1), + cache = []; + + while (++index < length) { + value = array[index]; + if (indexOf(result, value) < 0 && + every(others, function(other, index) { + return (cache[index] || (cache[index] = cachedContains(other)))(value); + })) { + result.push(value); + } + } + return result; + } /** - * Produces a sorted array of the enumerable properties, own and inherited, - * of `object` that have function values. + * Gets the last element of the `array`. Pass `n` to return the lasy `n` + * elementsvof the `array`. * * @static * @memberOf _ - * @alias methods - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names that have function values. + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Mixed} Returns the last element or an array of the last `n` + * elements of `array`. * @example * - * _.functions(_); - * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] + * _.last([3, 2, 1]); + * // => 1 */ - var functions = createIterator({ - 'useHas': false, - 'args': 'object', - 'init': '[]', - 'inLoop': 'if (toString.call(iteratee[index]) == funcClass) result.push(index)', - 'bottom': 'result.sort()' - }); + function last(array, n, guard) { + if (array) { + var length = array.length; + return (n == null || guard) ? array[length - 1] : slice.call(array, -n || length); + } + } /** - * Checks if the specified object `property` exists and is a direct property, - * instead of an inherited property. + * Gets the index at which the last occurrence of `value` is found using + * strict equality for comparisons, i.e. `===`. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to check. - * @param {String} property The property to check for. - * @returns {Boolean} Returns `true` if key is a direct property, else `false`. + * @category Arrays + * @param {Array} array The array to search. + * @param {Mixed} value The value to search for. + * @param {Number} [fromIndex=array.length-1] The index to start searching from. + * @returns {Number} Returns the index of the matched value or `-1`. * @example * - * _.has({ 'a': 1, 'b': 2, 'c': 3 }, 'b'); - * // => true + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2); + * // => 4 + * + * _.lastIndexOf([1, 2, 3, 1, 2, 3], 2, 3); + * // => 1 */ - function has(object, property) { - return hasOwnProperty.call(object, property); + function lastIndexOf(array, value, fromIndex) { + if (!array) { + return -1; + } + var index = array.length; + if (fromIndex && typeof fromIndex == 'number') { + index = (fromIndex < 0 ? Math.max(0, index + fromIndex) : Math.min(fromIndex, index - 1)) + 1; + } + while (index--) { + if (array[index] === value) { + return index; + } + } + return -1; } /** - * Checks if `value` is an `arguments` object. + * Retrieves the maximum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to + * `thisArg` and invoked with 3 arguments; (value, index, array). * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an `arguments` object, else `false`. + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the maximum value. * @example * - * (function() { return _.isArguments(arguments); })(1, 2, 3); - * // => true + * var stooges = [ + * { 'name': 'moe', 'age': 40 }, + * { 'name': 'larry', 'age': 50 }, + * { 'name': 'curly', 'age': 60 } + * ]; * - * _.isArguments([1, 2, 3]); - * // => false + * _.max(stooges, function(stooge) { return stooge.age; }); + * // => { 'name': 'curly', 'age': 60 }; */ - var isArguments = function(value) { - return toString.call(value) == '[object Arguments]'; - }; - // fallback for browser like Firefox < 4 and IE < 9 which detect - // `arguments` as `[object Object]` - if (!isArguments(arguments)) { - isArguments = function(value) { - return !!(value && hasOwnProperty.call(value, 'callee')); - }; + function max(array, callback, thisArg) { + var computed = -Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] > result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current > computed) { + computed = current; + result = array[index]; + } + } + return result; } /** - * Checks if `value` is an array. + * Retrieves the minimum value of an `array`. If `callback` is passed, + * it will be executed for each value in the `array` to generate the + * criterion by which the value is ranked. The `callback` is bound to `thisArg` + * and invoked with 3 arguments; (value, index, array). * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an array, else `false`. + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Function} [callback] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Mixed} Returns the minimum value. * @example * - * (function() { return _.isArray(arguments); })(); - * // => false - * - * _.isArray([1, 2, 3]); - * // => true + * _.min([10, 5, 100, 2, 1000]); + * // => 2 */ - var isArray = nativeIsArray || function(value) { - return toString.call(value) == arrayClass; - }; + function min(array, callback, thisArg) { + var computed = Infinity, + result = computed; + + if (!array) { + return result; + } + var current, + index = -1, + length = array.length; + + if (!callback) { + while (++index < length) { + if (array[index] < result) { + result = array[index]; + } + } + return result; + } + if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + while (++index < length) { + current = callback(array[index], index, array); + if (current < computed) { + computed = current; + result = array[index]; + } + } + return result; + } /** - * Checks if `value` is a boolean (`true` or `false`) value. + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to but not including `stop`. This method is a port of Python's + * `range()` function. See http://docs.python.org/library/functions.html#range. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a boolean value, else `false`. + * @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 descrement by. + * @returns {Array} Returns a new range array. * @example * - * _.isBoolean(null); - * // => false + * _.range(10); + * // => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + * + * _.range(1, 11); + * // => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + * + * _.range(0, 30, 5); + * // => [0, 5, 10, 15, 20, 25] + * + * _.range(0, -10, -1); + * // => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + * + * _.range(0); + * // => [] */ - function isBoolean(value) { - return value === true || value === false || toString.call(value) == boolClass; + function range(start, end, step) { + start = +start || 0; + step = +step || 1; + + if (end == null) { + end = start; + start = 0; + } + // use `Array(length)` so V8 will avoid the slower "dictionary" mode + // http://www.youtube.com/watch?v=XAqIpGU8ZZk#t=16m27s + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = Array(length); + + while (++index < length) { + result[index] = start; + start += step; + } + return result; } /** - * Checks if `value` is a date. + * The opposite of `_.initial`, this method gets all but the first value of + * `array`. Pass `n` to exclude the first `n` values from the result. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a date, else `false`. + * @alias tail + * @category Arrays + * @param {Array} array The array to query. + * @param {Number} [n] The number of elements to return. + * @param {Object} [guard] Internally used to allow this method to work with + * others like `_.map` without using their callback `index` argument for `n`. + * @returns {Array} Returns all but the first value or `n` values of `array`. * @example * - * _.isDate(new Date); - * // => true + * _.rest([3, 2, 1]); + * // => [2, 1] */ - function isDate(value) { - return toString.call(value) == dateClass; + function rest(array, n, guard) { + if (!array) { + return []; + } + return slice.call(array, (n == null || guard) ? 1 : n); } /** - * Checks if `value` is a DOM element. + * Creates a new array of shuffled `array` values, using a version of the + * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a DOM element, else `false`. + * @category Arrays + * @param {Array} array The array to shuffle. + * @returns {Array} Returns a new shuffled array. * @example * - * _.isElement(document.body); - * // => true + * _.shuffle([1, 2, 3, 4, 5, 6]); + * // => [4, 1, 6, 3, 5, 2] */ - function isElement(value) { - return !!(value && value.nodeType == 1); + function shuffle(array) { + if (!array) { + return []; + } + var rand, + index = -1, + length = array.length, + result = Array(length); + + while (++index < length) { + rand = Math.floor(Math.random() * (index + 1)); + result[index] = result[rand]; + result[rand] = array[index]; + } + return result; } /** - * Checks if `value` is empty. Arrays or strings with a length of `0` and - * objects with no own enumerable properties are considered "empty". + * Uses a binary search to determine the smallest index at which the `value` + * should be inserted into `array` in order to maintain the sort order of the + * sorted `array`. If `callback` is passed, it will be executed for `value` and + * each element in `array` to compute their sort ranking. The `callback` is + * bound to `thisArg` and invoked with 1 argument; (value). * * @static * @memberOf _ - * @category Objects - * @param {Array|Object|String} value The value to inspect. - * @returns {Boolean} Returns `true` if the `value` is empty, else `false`. + * @category Arrays + * @param {Array} array The array to iterate over. + * @param {Mixed} value The value to evaluate. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Number} Returns the index at which the value should be inserted + * into `array`. * @example * - * _.isEmpty([1, 2, 3]); - * // => false + * _.sortedIndex([20, 30, 40], 35); + * // => 2 * - * _.isEmpty({}); - * // => true + * var dict = { + * 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 } + * }; * - * _.isEmpty(''); - * // => true + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return dict.wordToNumber[word]; + * }); + * // => 2 + * + * _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) { + * return this.wordToNumber[word]; + * }, dict); + * // => 2 */ - var isEmpty = createIterator({ - 'args': 'value', - 'init': 'true', - 'top': - 'var className = toString.call(value);\n' + - 'if (className == arrayClass || className == stringClass) return !value.length', - 'inLoop': { - 'object': 'return false' + function sortedIndex(array, value, callback, thisArg) { + if (!array) { + return 0; } - }); + var mid, + low = 0, + high = array.length; + + if (callback) { + if (thisArg) { + callback = bind(callback, thisArg); + } + value = callback(value); + while (low < high) { + mid = (low + high) >>> 1; + callback(array[mid]) < value ? low = mid + 1 : high = mid; + } + } else { + while (low < high) { + mid = (low + high) >>> 1; + array[mid] < value ? low = mid + 1 : high = mid; + } + } + return low; + } /** - * Performs a deep comparison between two values to determine if they are - * equivalent to each other. + * Computes the union of the passed-in arrays using strict equality for + * comparisons, i.e. `===`. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} a The value to compare. - * @param {Mixed} b The other value to compare. - * @param {Array} [stack] Internally used to keep track of "seen" objects to - * avoid circular references. - * @returns {Boolean} Returns `true` if the values are equvalent, else `false`. + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of unique values, in order, that are + * present in one or more of the arrays. * @example * - * var moe = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * var clone = { 'name': 'moe', 'luckyNumbers': [13, 27, 34] }; - * - * moe == clone; - * // => false - * - * _.isEqual(moe, clone); - * // => true + * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); + * // => [1, 2, 3, 101, 10] */ - function isEqual(a, b, stack) { - stack || (stack = []); + function union() { + var index = -1, + result = [], + flattened = concat.apply(result, arguments), + length = flattened.length; - // exit early for identical values - if (a === b) { - // treat `+0` vs. `-0` as not equal - return a !== 0 || (1 / a == 1 / b); - } - // a strict comparison is necessary because `undefined == null` - if (a == null || b == null) { - return a === b; - } - // unwrap any wrapped objects - if (a._chain) { - a = a._wrapped; - } - if (b._chain) { - b = b._wrapped; - } - // invoke a custom `isEqual` method if one is provided - if (a.isEqual && toString.call(a.isEqual) == funcClass) { - return a.isEqual(b); - } - if (b.isEqual && toString.call(b.isEqual) == funcClass) { - return b.isEqual(a); - } - // compare [[Class]] names - var className = toString.call(a); - if (className != toString.call(b)) { - return false; + while (++index < length) { + if (indexOf(result, flattened[index]) < 0) { + result.push(flattened[index]); + } } - switch (className) { - // strings, numbers, dates, and booleans are compared by value - case stringClass: - // primitives and their corresponding object instances are equivalent; - // thus, `'5'` is quivalent to `new String('5')` - return a == String(b); - - case numberClass: - // treat `NaN` vs. `NaN` as equal - return a != +a - ? b != +b - // but treat `+0` vs. `-0` as not equal - : (a == 0 ? (1 / a == 1 / b) : a == +b); + return result; + } - case boolClass: - case dateClass: - // coerce dates and booleans to numeric values, dates to milliseconds and - // booleans to 1 or 0; treat invalid dates coerced to `NaN` as not equal - return +a == +b; + /** + * Creates a duplicate-value-free version of the `array` using strict equality + * for comparisons, i.e. `===`. If the `array` is already sorted, passing `true` + * for `isSorted` will run a faster algorithm. If `callback` is passed, each + * element of `array` is passed through a callback` before uniqueness is computed. + * The `callback` is bound to `thisArg` and invoked with 3 arguments; (value, index, array). + * + * @static + * @memberOf _ + * @alias unique + * @category Arrays + * @param {Array} array The array to process. + * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a duplicate-value-free array. + * @example + * + * _.uniq([1, 2, 1, 3, 1]); + * // => [1, 2, 3] + * + * _.uniq([1, 1, 2, 2, 3], true); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); }); + * // => [1, 2, 3] + * + * _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math); + * // => [1, 2, 3] + */ + function uniq(array, isSorted, callback, thisArg) { + var result = []; + if (!array) { + return result; + } + var computed, + index = -1, + length = array.length, + seen = []; - // regexps are compared by their source and flags - case regexpClass: - return a.source == b.source && - a.global == b.global && - a.multiline == b.multiline && - a.ignoreCase == b.ignoreCase; + // juggle arguments + if (typeof isSorted == 'function') { + thisArg = callback; + callback = isSorted; + isSorted = false; } - if (typeof a != 'object' || typeof b != 'object') { - return false; + if (!callback) { + callback = identity; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); } - // Assume equality for cyclic structures. The algorithm for detecting cyclic - // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. - var length = stack.length; - while (length--) { - // Linear search. Performance is inversely proportional to the number of - // unique nested structures. - if (stack[length] == a) { - return true; + while (++index < length) { + computed = callback(array[index], index, array); + if (isSorted + ? !index || seen[seen.length - 1] !== computed + : indexOf(seen, computed) < 0 + ) { + seen.push(computed); + result.push(array[index]); } } + return result; + } + /** + * Creates a new array with all occurrences of the passed values removed using + * strict equality for comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to filter. + * @param {Mixed} [value1, value2, ...] Values to remove. + * @returns {Array} Returns a new filtered array. + * @example + * + * _.without([1, 2, 1, 0, 3, 1, 4], 0, 1); + * // => [2, 3, 4] + */ + function without(array) { + var result = []; + if (!array) { + return result; + } var index = -1, - result = true, - size = 0; + length = array.length, + contains = cachedContains(arguments, 1, 20); - // add the first collection to the stack of traversed objects - stack.push(a); + while (++index < length) { + if (!contains(array[index])) { + result.push(array[index]); + } + } + return result; + } - // recursively compare objects and arrays - if (className == arrayClass) { - // compare array lengths to determine if a deep comparison is necessary - size = a.length; - result = size == b.length; + /** + * Groups the elements of each array at their corresponding indexes. Useful for + * separate data sources that are coordinated through matching array indexes. + * For a matrix of nested arrays, `_.zip.apply(...)` can transpose the matrix + * in a similar fashion. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} [array1, array2, ...] Arrays to process. + * @returns {Array} Returns a new array of grouped elements. + * @example + * + * _.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); + * // => [['moe', 30, true], ['larry', 40, false], ['curly', 50, false]] + */ + function zip(array) { + if (!array) { + return []; + } + var index = -1, + length = max(pluck(arguments, 'length')), + result = Array(length); - if (result) { - // deep compare the contents, ignoring non-numeric properties - while (size--) { - if (!(result = isEqual(a[size], b[size], stack))) { - break; - } - } - } + while (++index < length) { + result[index] = pluck(arguments, index); } - else { - // objects with different constructors are not equivalent - if ('constructor' in a != 'constructor' in b || a.constructor != b.constructor) { - return false; - } - // deep compare objects. - for (var prop in a) { - if (hasOwnProperty.call(a, prop)) { - // count the number of properties. - size++; - // deep compare each property value. - if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { - break; - } - } - } - // ensure both objects have the same number of properties - if (result) { - for (prop in b) { - // Adobe's JS engine, embedded in applications like InDesign, has a - // bug that causes `!size--` to throw an error so it must be wrapped - // in parentheses. - // https://github.com/documentcloud/underscore/issues/355 - if (hasOwnProperty.call(b, prop) && !(size--)) { - break; - } - } - result = !size; - } - // handle JScript [[DontEnum]] bug - if (result && hasDontEnumBug) { - while (++index < 7) { - prop = shadowed[index]; - if (hasOwnProperty.call(a, prop)) { - if (!(result = hasOwnProperty.call(b, prop) && isEqual(a[prop], b[prop], stack))) { - break; - } - } - } - } + return result; + } + + /** + * Creates an object composed from an array of `keys` and an array of `values`. + * + * @static + * @memberOf _ + * @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. + * @example + * + * _.zipObject(['moe', 'larry', 'curly'], [30, 40, 50]); + * // => { 'moe': 30, 'larry': 40, 'curly': 50 } + */ + function zipObject(keys, values) { + if (!keys) { + return {}; + } + var index = -1, + length = keys.length, + result = {}; + + values || (values = []); + while (++index < length) { + result[keys[index]] = values[index]; } - // remove the first collection from the stack of traversed objects - stack.pop(); return result; } + /*--------------------------------------------------------------------------*/ + /** - * Checks if `value` is a finite number. - * Note: This is not the same as native `isFinite`, which will return true for - * booleans and other values. See http://es5.github.com/#x15.1.2.5. + * Creates a new function that is restricted to executing only after it is + * called `n` times. * - * @deprecated * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a finite number, else `false`. + * @category Functions + * @param {Number} n The number of times the function must be called before + * it is executed. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. * @example * - * _.isFinite(-101); - * // => true - * - * _.isFinite('10'); - * // => false - * - * _.isFinite(Infinity); - * // => false + * var renderNotes = _.after(notes.length, render); + * _.forEach(notes, function(note) { + * note.asyncSave({ 'success': renderNotes }); + * }); + * // `renderNotes` is run once, after all notes have saved */ - function isFinite(value) { - return nativeIsFinite(value) && toString.call(value) == numberClass; + function after(n, func) { + if (n < 1) { + return func(); + } + return function() { + if (--n < 1) { + return func.apply(this, arguments); + } + }; } /** - * Checks if `value` is a function. + * Creates a new function that, when called, invokes `func` with the `this` + * binding of `thisArg` and prepends any additional `bind` arguments to those + * passed to the bound function. Lazy defined methods may be bound by passing + * the object they are bound to as `func` and the method name as `thisArg`. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a function, else `false`. + * @category Functions + * @param {Function|Object} func The function to bind or the object the method belongs to. + * @param {Mixed} [thisArg] The `this` binding of `func` or the method name. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new bound function. * @example * - * _.isFunction(''.concat); - * // => true + * // basic bind + * var func = function(greeting) { + * return greeting + ' ' + this.name; + * }; + * + * func = _.bind(func, { 'name': 'moe' }, 'hi'); + * func(); + * // => 'hi moe' + * + * // lazy bind + * var object = { + * 'name': 'moe', + * 'greet': function(greeting) { + * return greeting + ' ' + this.name; + * } + * }; + * + * var func = _.bind(object, 'greet', 'hi'); + * func(); + * // => 'hi moe' + * + * object.greet = function(greeting) { + * return greeting + ', ' + this.name + '!'; + * }; + * + * func(); + * // => 'hi, moe!' */ - function isFunction(value) { - return toString.call(value) == funcClass; + function bind(func, thisArg) { + var methodName, + isFunc = isFunction(func); + + // juggle arguments + if (!isFunc) { + methodName = thisArg; + thisArg = func; + } + // use `Function#bind` if it exists and is fast + // (in V8 `Function#bind` is slower except when partially applied) + else if (isBindFast || (nativeBind && arguments.length > 2)) { + return nativeBind.call.apply(nativeBind, arguments); + } + + var partialArgs = slice.call(arguments, 2); + + function bound() { + // `Function#bind` spec + // http://es5.github.com/#x15.3.4.5 + var args = arguments, + thisBinding = thisArg; + + if (!isFunc) { + func = thisArg[methodName]; + } + if (partialArgs.length) { + args = args.length + ? concat.apply(partialArgs, args) + : partialArgs; + } + if (this instanceof bound) { + // get `func` instance if `bound` is invoked in a `new` expression + noop.prototype = func.prototype; + thisBinding = new noop; + + // mimic the constructor's `return` behavior + // http://es5.github.com/#x13.2.2 + var result = func.apply(thisBinding, args); + return result && objectTypes[typeof result] + ? result + : thisBinding + } + return func.apply(thisBinding, args); + } + return bound; } /** - * Checks if `value` is the language type of Object. - * (e.g. arrays, functions, objects, regexps, `new Number(0)`, and `new String('')`) + * Binds methods on `object` to `object`, overwriting the existing method. + * If no method names are provided, all the function properties of `object` + * will be bound. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is an object, else `false`. + * @category Functions + * @param {Object} object The object to bind and assign the bound methods to. + * @param {String} [methodName1, methodName2, ...] Method names on the object to bind. + * @returns {Object} Returns `object`. * @example * - * _.isObject({}); - * // => true + * var buttonView = { + * 'label': 'lodash', + * 'onClick': function() { alert('clicked: ' + this.label); } + * }; * - * _.isObject(1); - * // => false + * _.bindAll(buttonView); + * jQuery('#lodash_button').on('click', buttonView.onClick); + * // => When the button is clicked, `this.label` will have the correct value */ - function isObject(value) { - // check if the value is the ECMAScript language type of Object - // http://es5.github.com/#x8 - return value && objectTypes[typeof value]; - } + var bindAll = createIterator({ + 'useHas': false, + 'useStrict': false, + 'args': 'object', + 'init': 'object', + 'top': + 'var funcs = arguments,\n' + + ' length = funcs.length;\n' + + 'if (length > 1) {\n' + + ' for (var index = 1; index < length; index++) {\n' + + ' result[funcs[index]] = bind(result[funcs[index]], result)\n' + + ' }\n' + + ' return result\n' + + '}', + 'inLoop': + 'if (isFunction(result[index])) {\n' + + ' result[index] = bind(result[index], result)\n' + + '}' + }); /** - * Checks if `value` is `NaN`. - * Note: This is not the same as native `isNaN`, which will return true for - * `undefined` and other values. See http://es5.github.com/#x15.1.2.4. + * Creates a new function that is the composition of the passed functions, + * where each function consumes the return value of the function that follows. + * In math terms, composing the functions `f()`, `g()`, and `h()` produces `f(g(h()))`. * - * @deprecated * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `NaN`, else `false`. + * @category Functions + * @param {Function} [func1, func2, ...] Functions to compose. + * @returns {Function} Returns the new composed function. * @example * - * _.isNaN(NaN); - * // => true - * - * _.isNaN(new Number(NaN)); - * // => true - * - * isNaN(undefined); - * // => true - * - * _.isNaN(undefined); - * // => false + * var greet = function(name) { return 'hi: ' + name; }; + * var exclaim = function(statement) { return statement + '!'; }; + * var welcome = _.compose(exclaim, greet); + * welcome('moe'); + * // => 'hi: moe!' */ - 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) - return toString.call(value) == numberClass && value != +value + function compose() { + var funcs = arguments; + return function() { + var args = arguments, + length = funcs.length; + + while (length--) { + args = [funcs[length].apply(this, args)]; + } + return args[0]; + }; } /** - * Checks if `value` is `null`. + * Creates a new function that will delay the execution of `func` until after + * `wait` milliseconds have elapsed since the last time it was invoked. Pass + * `true` for `immediate` to cause debounce to invoke `func` on the leading, + * instead of the trailing, edge of the `wait` timeout. Subsequent calls to + * the debounced function will return the result of the last `func` call. * - * @deprecated * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `null`, else `false`. + * @category Functions + * @param {Function} func The function to debounce. + * @param {Number} wait The number of milliseconds to delay. + * @param {Boolean} immediate A flag to indicate execution is on the leading + * edge of the timeout. + * @returns {Function} Returns the new debounced function. * @example * - * _.isNull(null); - * // => true - * - * _.isNull(undefined); - * // => false + * var lazyLayout = _.debounce(calculateLayout, 300); + * jQuery(window).on('resize', lazyLayout); */ - function isNull(value) { - return value === null; + function debounce(func, wait, immediate) { + var args, + result, + thisArg, + timeoutId; + + function delayed() { + timeoutId = null; + if (!immediate) { + func.apply(thisArg, args); + } + } + + return function() { + var isImmediate = immediate && !timeoutId; + args = arguments; + thisArg = this; + + clearTimeout(timeoutId); + timeoutId = setTimeout(delayed, wait); + + if (isImmediate) { + result = func.apply(thisArg, args); + } + return result; + }; } /** - * Checks if `value` is a number. + * Executes the `func` function after `wait` milliseconds. Additional arguments + * will be passed to `func` when it is invoked. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a number, else `false`. + * @category Functions + * @param {Function} func The function to delay. + * @param {Number} wait The number of milliseconds to delay execution. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. * @example * - * _.isNumber(8.4 * 5; - * // => true + * var log = _.bind(console.log, console); + * _.delay(log, 1000, 'logged later'); + * // => 'logged later' (Appears after one second.) */ - function isNumber(value) { - return toString.call(value) == numberClass; + function delay(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function() { return func.apply(undefined, args); }, wait); } /** - * Checks if `value` is a regular expression. + * Defers executing the `func` function until the current call stack has cleared. + * Additional arguments will be passed to `func` when it is invoked. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a regular expression, else `false`. + * @category Functions + * @param {Function} func The function to defer. + * @param {Mixed} [arg1, arg2, ...] Arguments to invoke the function with. + * @returns {Number} Returns the `setTimeout` timeout id. * @example * - * _.isRegExp(/moe/); - * // => true + * _.defer(function() { alert('deferred'); }); + * // returns from the function before `alert` is called */ - function isRegExp(value) { - return toString.call(value) == regexpClass; + function defer(func) { + var args = slice.call(arguments, 1); + return setTimeout(function() { return func.apply(undefined, args); }, 1); } /** - * Checks if `value` is a string. + * Creates a new function that memoizes the result of `func`. If `resolver` is + * passed, it will be used to determine the cache key for storing the result + * based on the arguments passed to the memoized function. By default, the first + * argument passed to the memoized function is used as the cache key. * * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is a string, else `false`. + * @category Functions + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] A function used to resolve the cache key. + * @returns {Function} Returns the new memoizing function. * @example * - * _.isString('moe'); - * // => true + * var fibonacci = _.memoize(function(n) { + * return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2); + * }); */ - function isString(value) { - return toString.call(value) == stringClass; + function memoize(func, resolver) { + var cache = {}; + return function() { + var prop = resolver ? resolver.apply(this, arguments) : arguments[0]; + return hasOwnProperty.call(cache, prop) + ? cache[prop] + : (cache[prop] = func.apply(this, arguments)); + }; } /** - * Checks if `value` is `undefined`. + * Creates a new function that is restricted to one execution. Repeat calls to + * the function will return the value of the first call. * - * @deprecated * @static * @memberOf _ - * @category Objects - * @param {Mixed} value The value to check. - * @returns {Boolean} Returns `true` if the `value` is `undefined`, else `false`. + * @category Functions + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. * @example * - * _.isUndefined(void 0); - * // => true + * var initialize = _.once(createApplication); + * initialize(); + * initialize(); + * // Application is only created once. */ - function isUndefined(value) { - return value === undefined; + function once(func) { + var result, + ran = false; + + return function() { + if (ran) { + return result; + } + ran = true; + result = func.apply(this, arguments); + + // clear the `func` variable so the function may be garbage collected + func = null; + return result; + }; } /** - * Produces an array of object`'s own enumerable property names. + * Creates a new function that, when called, invokes `func` with any additional + * `partial` arguments prepended to those passed to the new function. This method + * is similar `bind`, except it does **not** alter the `this` binding. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @category Functions + * @param {Function} func The function to partially apply arguments to. + * @param {Mixed} [arg1, arg2, ...] Arguments to be partially applied. + * @returns {Function} Returns the new partially applied function. * @example * - * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); - * // => ['one', 'two', 'three'] (order is not guaranteed) + * var greet = function(greeting, name) { return greeting + ': ' + name; }; + * var hi = _.partial(greet, 'hi'); + * hi('moe'); + * // => 'hi: moe' */ - var keys = !nativeKeys ? shimKeys : function(object) { - // avoid iterating over the `prototype` property - return typeof object == 'function' && propertyIsEnumerable.call(object, 'prototype') - ? shimKeys(object) - : nativeKeys(object); - }; + function partial(func) { + var args = slice.call(arguments, 1), + argsLength = args.length; - /** - * Creates an object composed of the specified properties. Property names may - * be specified as individual arguments or as arrays of property names. - * - * @static - * @memberOf _ - * @category Objects - * @param {Object} object The object to pluck. - * @param {Object} [prop1, prop2, ...] The properties to pick. - * @returns {Object} Returns an object composed of the picked properties. - * @example - * - * _.pick({ 'name': 'moe', 'age': 40, 'userid': 'moe1' }, 'name', 'age'); - * // => { 'name': 'moe', 'age': 40 } - */ - function pick(object) { - var prop, - index = 0, - props = concat.apply(ArrayProto, arguments), - length = props.length, - result = {}; + return function() { + var result, + others = arguments; - // start `index` at `1` to skip `object` - while (++index < length) { - prop = props[index]; - if (prop in object) { - result[prop] = object[prop]; + if (others.length) { + args.length = argsLength; + push.apply(args, others); } - } - return result; + result = args.length == 1 ? func.call(this, args[0]) : func.apply(this, args); + args.length = argsLength; + return result; + }; } /** - * Gets the size of `value` by returning `value.length` if `value` is a string - * or array, or the number of own enumerable properties if `value` is an object. + * Creates a new function that, when executed, will only call the `func` + * function at most once per every `wait` milliseconds. If the throttled + * function is invoked more than once during the `wait` timeout, `func` will + * also be called on the trailing edge of the timeout. Subsequent calls to the + * throttled function will return the result of the last `func` call. * - * @deprecated * @static * @memberOf _ - * @category Objects - * @param {Array|Object|String} value The value to inspect. - * @returns {Number} Returns `value.length` if `value` is a string or array, - * or the number of own enumerable properties if `value` is an object. + * @category Functions + * @param {Function} func The function to throttle. + * @param {Number} wait The number of milliseconds to throttle executions to. + * @returns {Function} Returns the new throttled function. * @example * - * _.size([1, 2]); - * // => 2 - * - * _.size({ 'one': 1, 'two': 2, 'three': 3 }); - * // => 3 - * - * _.size('curly'); - * // => 5 + * var throttled = _.throttle(updatePosition, 100); + * jQuery(window).on('scroll', throttled); */ - function size(value) { - if (!value) { - return 0; + function throttle(func, wait) { + var args, + result, + thisArg, + timeoutId, + lastCalled = 0; + + function trailingCall() { + lastCalled = new Date; + timeoutId = null; + func.apply(thisArg, args); } - var length = value.length; - return length === length >>> 0 ? value.length : keys(value).length; + + return function() { + var now = new Date, + remain = wait - (now - lastCalled); + + args = arguments; + thisArg = this; + + if (remain <= 0) { + lastCalled = now; + result = func.apply(thisArg, args); + } + else if (!timeoutId) { + timeoutId = setTimeout(trailingCall, remain); + } + return result; + }; } /** - * Produces an array of `object`'s own enumerable property values. + * Creates a new function that passes `value` to the `wrapper` function as its + * first argument. Additional arguments passed to the new function are appended + * to those passed to the `wrapper` function. * * @static * @memberOf _ - * @category Objects - * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property values. + * @category Functions + * @param {Mixed} value The value to wrap. + * @param {Function} wrapper The wrapper function. + * @returns {Function} Returns the new function. * @example * - * _.values({ 'one': 1, 'two': 2, 'three': 3 }); - * // => [1, 2, 3] + * var hello = function(name) { return 'hello: ' + name; }; + * hello = _.wrap(hello, function(func) { + * return 'before, ' + func('moe') + ', after'; + * }); + * hello(); + * // => 'before, hello: moe, after' */ - var values = createIterator({ - 'args': 'object', - 'init': '[]', - 'inLoop': 'result.push(iteratee[index])' - }); + function wrap(value, wrapper) { + return function() { + var args = [value]; + if (arguments.length) { + push.apply(args, arguments); + } + return wrapper.apply(this, args); + }; + } /*--------------------------------------------------------------------------*/ @@ -3167,8 +3596,8 @@ * @returns {String} Returns the escaped string. * @example * - * _.escape('Curly, Larry & Moe'); - * // => "Curly, Larry & Moe" + * _.escape('Moe, Larry & Curly'); + * // => "Moe, Larry & Curly" */ function escape(string) { return string == null ? '' : (string + '').replace(reUnescapedHtml, escapeHtmlChar); @@ -3176,6 +3605,7 @@ /** * This function returns the first argument passed to it. + * * Note: It is used throughout Lo-Dash as a default callback. * * @static @@ -3209,11 +3639,11 @@ * } * }); * - * _.capitalize('curly'); - * // => 'Curly' - * - * _('larry').capitalize(); + * _.capitalize('larry'); * // => 'Larry' + * + * _('curly').capitalize(); + * // => 'Curly' */ function mixin(object) { forEach(functions(object), function(methodName) { @@ -3285,13 +3715,16 @@ return null; } var value = object[property]; - return toString.call(value) == funcClass ? object[property]() : value; + return isFunction(value) ? object[property]() : value; } /** * A micro-templating method that handles arbitrary delimiters, preserves * whitespace, and correctly escapes quotes within interpolated code. * + * Note: For Chrome extensions use the `lodash csp` build and see + * http://code.google.com/chrome/extensions/trunk/sandboxingEval.html + * * @static * @memberOf _ * @category Utilities @@ -3308,8 +3741,8 @@ * // => 'hello: moe' * * var list = '<% _.forEach(people, function(name) { %>
  • <%= name %>
  • <% }); %>'; - * _.template(list, { 'people': ['moe', 'curly', 'larry'] }); - * // => '
  • moe
  • curly
  • larry
  • ' + * _.template(list, { 'people': ['moe', 'larry', 'curly'] }); + * // => '
  • moe
  • larry
  • curly
  • ' * * var template = _.template('<%- value %>'); * template({ 'value': ' - + diff --git a/perf/perf.js b/perf/perf.js index f945ba866f..b30f776e8b 100644 --- a/perf/perf.js +++ b/perf/perf.js @@ -132,11 +132,14 @@ // potentially expensive for (index = 0; index < this.count; index++) { - bindAllObjects[index] = belt.clone(lodash); + bindAllObjects[index] = belt.reduce(funcNames, function(object, funcName) { + object[funcName] = lodash[funcName]; + return object; + }, {}); } } - if (typeof groupBy != 'undefined') { + if (typeof countBy != 'undefined') { var wordToNumber = { 'one': 1, 'two': 2, @@ -515,6 +518,62 @@ /*--------------------------------------------------------------------------*/ + suites.push( + Benchmark.Suite('`_.clone` with an object') + .add('Lo-Dash', function() { + lodash.clone(object); + }) + .add('Underscore', function() { + _.clone(object); + }) + ); + + /*--------------------------------------------------------------------------*/ + + suites.push( + Benchmark.Suite('`_.countBy` with `callback` iterating an array') + .add('Lo-Dash', function() { + lodash.countBy(numbers, function(num) { return num >> 1; }); + }) + .add('Underscore', function() { + _.countBy(numbers, function(num) { return num >> 1; }); + }) + ); + + suites.push( + Benchmark.Suite('`_.countBy` with `property` name iterating an array') + .add('Lo-Dash', { + 'fn': function() { + lodash.countBy(words, 'length'); + }, + 'teardown': 'function countBy(){}' + }) + .add('Underscore', { + 'fn': function() { + _.countBy(words, 'length'); + }, + 'teardown': 'function countBy(){}' + }) + ); + + suites.push( + Benchmark.Suite('`_.countBy` with `callback` iterating an object') + .add('Lo-Dash', { + 'fn': function() { + lodash.countBy(wordToNumber, function(num) { return num >> 1; }); + }, + 'teardown': 'function countBy(){}' + }) + .add('Underscore', { + 'fn': function() { + _.countBy(wordToNumber, function(num) { return num >> 1; }); + }, + 'teardown': 'function countBy(){}' + }) + ); + + /*--------------------------------------------------------------------------*/ + suites.push( Benchmark.Suite('`_.difference`') .add('Lo-Dash', function() { @@ -749,13 +808,13 @@ 'fn': function() { lodash.groupBy(words, 'length'); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) .add('Underscore', { 'fn': function() { _.groupBy(words, 'length'); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) ); @@ -765,13 +824,13 @@ 'fn': function() { lodash.groupBy(wordToNumber, function(num) { return num >> 1; }); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) .add('Underscore', { 'fn': function() { _.groupBy(wordToNumber, function(num) { return num >> 1; }); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) ); @@ -1101,16 +1160,6 @@ /*--------------------------------------------------------------------------*/ - suites.push( - Benchmark.Suite('`_.size` with an array') - .add('Lo-Dash', function() { - lodash.size(numbers); - }) - .add('Underscore', function() { - _.size(numbers); - }) - ); - suites.push( Benchmark.Suite('`_.size` with an object') .add('Lo-Dash', function() { @@ -1149,13 +1198,13 @@ 'fn': function() { lodash.sortBy(words, 'length'); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) .add('Underscore', { 'fn': function() { _.sortBy(words, 'length'); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) ); @@ -1179,7 +1228,7 @@ return wordToNumber[value]; }); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) .add('Underscore', { 'fn': function() { @@ -1187,7 +1236,7 @@ return wordToNumber[value]; }); }, - 'teardown': 'function groupBy(){}' + 'teardown': 'function countBy(){}' }) ); diff --git a/test/test-ui.js b/test/test-ui.js index b4142ae94f..5b8ee929b4 100644 --- a/test/test-ui.js +++ b/test/test-ui.js @@ -70,10 +70,10 @@ var label1 = document.createElement('label'); label1.innerHTML = - 'No RequireJS'; + 'No RequireJS'; var label2 = document.createElement('label'); - label2.innerHTML = ' ' + + label2.innerHTML = ' ' + '