diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..48ecc30c3 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +raw \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 000000000..2ce268489 --- /dev/null +++ b/.npmignore @@ -0,0 +1,3 @@ +test/ +Rakefile +docs/ \ No newline at end of file diff --git a/CNAME b/CNAME new file mode 100644 index 000000000..a007e65c4 --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +underscorejs.org diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..61d28c08e --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2009-2012 Jeremy Ashkenas, DocumentCloud + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md index e69de29bb..a6564a2a2 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,19 @@ + __ + /\ \ __ + __ __ ___ \_\ \ __ _ __ ____ ___ ___ _ __ __ /\_\ ____ + /\ \/\ \ /' _ `\ /'_ \ /'__`\/\ __\/ ,__\ / ___\ / __`\/\ __\/'__`\ \/\ \ /',__\ + \ \ \_\ \/\ \/\ \/\ \ \ \/\ __/\ \ \//\__, `\/\ \__//\ \ \ \ \ \//\ __/ __ \ \ \/\__, `\ + \ \____/\ \_\ \_\ \___,_\ \____\\ \_\\/\____/\ \____\ \____/\ \_\\ \____\/\_\ _\ \ \/\____/ + \/___/ \/_/\/_/\/__,_ /\/____/ \/_/ \/___/ \/____/\/___/ \/_/ \/____/\/_//\ \_\ \/___/ + \ \____/ + \/___/ + +Underscore.js is a utility-belt library for JavaScript that provides +support for the usual functional suspects (each, map, reduce, filter...) +without extending any core JavaScript objects. + +For Docs, License, Tests, and pre-packed downloads, see: +http://documentcloud.github.com/underscore/ + +Many thanks to our contributors: +https://github.com/documentcloud/underscore/contributors diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..951663b7b --- /dev/null +++ b/Rakefile @@ -0,0 +1,20 @@ +require 'rubygems' +require 'closure-compiler' + +HEADER = /((^\s*\/\/.*\n)+)/ + +desc "Use the Closure Compiler to compress Underscore.js" +task :build do + source = File.read('underscore.js') + header = source.match(HEADER) + min = Closure::Compiler.new.compress(source) + File.open('underscore-min.js', 'w') do |file| + file.write header[1].squeeze(' ') + min + end +end + +desc "Build the docco documentation" +task :doc do + sh "docco underscore.js" +end + diff --git a/docs/docco.css b/docs/docco.css new file mode 100644 index 000000000..5aa0a8d73 --- /dev/null +++ b/docs/docco.css @@ -0,0 +1,186 @@ +/*--------------------- Layout and Typography ----------------------------*/ +body { + font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; + font-size: 15px; + line-height: 22px; + color: #252519; + margin: 0; padding: 0; +} +a { + color: #261a3b; +} + a:visited { + color: #261a3b; + } +p { + margin: 0 0 15px 0; +} +h1, h2, h3, h4, h5, h6 { + margin: 0px 0 15px 0; +} + h1 { + margin-top: 40px; + } +#container { + position: relative; +} +#background { + position: fixed; + top: 0; left: 525px; right: 0; bottom: 0; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + z-index: -1; +} +#jump_to, #jump_page { + background: white; + -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; + -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; + font: 10px Arial; + text-transform: uppercase; + cursor: pointer; + text-align: right; +} +#jump_to, #jump_wrapper { + position: fixed; + right: 0; top: 0; + padding: 5px 10px; +} + #jump_wrapper { + padding: 0; + display: none; + } + #jump_to:hover #jump_wrapper { + display: block; + } + #jump_page { + padding: 5px 0 3px; + margin: 0 0 25px 25px; + } + #jump_page .source { + display: block; + padding: 5px 10px; + text-decoration: none; + border-top: 1px solid #eee; + } + #jump_page .source:hover { + background: #f5f5ff; + } + #jump_page .source:first-child { + } +table td { + border: 0; + outline: 0; +} + td.docs, th.docs { + max-width: 450px; + min-width: 450px; + min-height: 5px; + padding: 10px 25px 1px 50px; + overflow-x: hidden; + vertical-align: top; + text-align: left; + } + .docs pre { + margin: 15px 0 15px; + padding-left: 15px; + } + .docs p tt, .docs p code { + background: #f8f8ff; + border: 1px solid #dedede; + font-size: 12px; + padding: 0 0.2em; + } + .pilwrap { + position: relative; + } + .pilcrow { + font: 12px Arial; + text-decoration: none; + color: #454545; + position: absolute; + top: 3px; left: -20px; + padding: 1px 2px; + opacity: 0; + -webkit-transition: opacity 0.2s linear; + } + td.docs:hover .pilcrow { + opacity: 1; + } + td.code, th.code { + padding: 14px 15px 16px 25px; + width: 100%; + vertical-align: top; + background: #f5f5ff; + border-left: 1px solid #e5e5ee; + } + pre, tt, code { + font-size: 12px; line-height: 18px; + font-family: Monaco, Consolas, "Lucida Console", monospace; + margin: 0; padding: 0; + } + + +/*---------------------- Syntax Highlighting -----------------------------*/ +td.linenos { background-color: #f0f0f0; padding-right: 10px; } +span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } +body .hll { background-color: #ffffcc } +body .c { color: #408080; font-style: italic } /* Comment */ +body .err { border: 1px solid #FF0000 } /* Error */ +body .k { color: #954121 } /* Keyword */ +body .o { color: #666666 } /* Operator */ +body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +body .cp { color: #BC7A00 } /* Comment.Preproc */ +body .c1 { color: #408080; font-style: italic } /* Comment.Single */ +body .cs { color: #408080; font-style: italic } /* Comment.Special */ +body .gd { color: #A00000 } /* Generic.Deleted */ +body .ge { font-style: italic } /* Generic.Emph */ +body .gr { color: #FF0000 } /* Generic.Error */ +body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +body .gi { color: #00A000 } /* Generic.Inserted */ +body .go { color: #808080 } /* Generic.Output */ +body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +body .gs { font-weight: bold } /* Generic.Strong */ +body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +body .gt { color: #0040D0 } /* Generic.Traceback */ +body .kc { color: #954121 } /* Keyword.Constant */ +body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ +body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ +body .kp { color: #954121 } /* Keyword.Pseudo */ +body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ +body .kt { color: #B00040 } /* Keyword.Type */ +body .m { color: #666666 } /* Literal.Number */ +body .s { color: #219161 } /* Literal.String */ +body .na { color: #7D9029 } /* Name.Attribute */ +body .nb { color: #954121 } /* Name.Builtin */ +body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +body .no { color: #880000 } /* Name.Constant */ +body .nd { color: #AA22FF } /* Name.Decorator */ +body .ni { color: #999999; font-weight: bold } /* Name.Entity */ +body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +body .nf { color: #0000FF } /* Name.Function */ +body .nl { color: #A0A000 } /* Name.Label */ +body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +body .nt { color: #954121; font-weight: bold } /* Name.Tag */ +body .nv { color: #19469D } /* Name.Variable */ +body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +body .w { color: #bbbbbb } /* Text.Whitespace */ +body .mf { color: #666666 } /* Literal.Number.Float */ +body .mh { color: #666666 } /* Literal.Number.Hex */ +body .mi { color: #666666 } /* Literal.Number.Integer */ +body .mo { color: #666666 } /* Literal.Number.Oct */ +body .sb { color: #219161 } /* Literal.String.Backtick */ +body .sc { color: #219161 } /* Literal.String.Char */ +body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ +body .s2 { color: #219161 } /* Literal.String.Double */ +body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +body .sh { color: #219161 } /* Literal.String.Heredoc */ +body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +body .sx { color: #954121 } /* Literal.String.Other */ +body .sr { color: #BB6688 } /* Literal.String.Regex */ +body .s1 { color: #219161 } /* Literal.String.Single */ +body .ss { color: #19469D } /* Literal.String.Symbol */ +body .bp { color: #954121 } /* Name.Builtin.Pseudo */ +body .vc { color: #19469D } /* Name.Variable.Class */ +body .vg { color: #19469D } /* Name.Variable.Global */ +body .vi { color: #19469D } /* Name.Variable.Instance */ +body .il { color: #666666 } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100755 index 000000000..030496838 Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/images/background.png b/docs/images/background.png new file mode 100644 index 000000000..90ee6934d Binary files /dev/null and b/docs/images/background.png differ diff --git a/docs/images/underscore.png b/docs/images/underscore.png new file mode 100644 index 000000000..dce9edb0f Binary files /dev/null and b/docs/images/underscore.png differ diff --git a/docs/underscore.html b/docs/underscore.html new file mode 100644 index 000000000..ccdb2d3f5 --- /dev/null +++ b/docs/underscore.html @@ -0,0 +1,631 @@ +
underscore.js | |
|---|---|
| (function() { |
Baseline setup | |
| Establish the root object, | var root = this; |
| Save the previous value of the | var previousUnderscore = root._; |
| Establish the object that gets returned to break out of a loop iteration. | var breaker = {}; |
| Save bytes in the minified (but not gzipped) version: | var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; |
| Create quick reference variables for speed access to core prototypes. | var slice = ArrayProto.slice,
+ unshift = ArrayProto.unshift,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty; |
| All ECMAScript 5 native function implementations that we hope to use +are declared here. | var
+ nativeForEach = ArrayProto.forEach,
+ nativeMap = ArrayProto.map,
+ nativeReduce = ArrayProto.reduce,
+ nativeReduceRight = ArrayProto.reduceRight,
+ nativeFilter = ArrayProto.filter,
+ nativeEvery = ArrayProto.every,
+ nativeSome = ArrayProto.some,
+ nativeIndexOf = ArrayProto.indexOf,
+ nativeLastIndexOf = ArrayProto.lastIndexOf,
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind; |
| Create a safe reference to the Underscore object for use below. | var _ = function(obj) { return new wrapper(obj); }; |
| Export the Underscore object for Node.js, with
+backwards-compatibility for the old | if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root['_'] = _;
+ } |
| Current version. | _.VERSION = '1.3.1'; |
Collection Functions | |
| The cornerstone, an | var each = _.each = _.forEach = function(obj, iterator, context) {
+ if (obj == null) return;
+ if (nativeForEach && obj.forEach === nativeForEach) {
+ obj.forEach(iterator, context);
+ } else if (obj.length === +obj.length) {
+ for (var i = 0, l = obj.length; i < l; i++) {
+ if (i in obj && iterator.call(context, obj[i], i, obj) === breaker) return;
+ }
+ } else {
+ for (var key in obj) {
+ if (_.has(obj, key)) {
+ if (iterator.call(context, obj[key], key, obj) === breaker) return;
+ }
+ }
+ }
+ }; |
| Return the results of applying the iterator to each element.
+Delegates to ECMAScript 5's native | _.map = _.collect = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
+ each(obj, function(value, index, list) {
+ results[results.length] = iterator.call(context, value, index, list);
+ });
+ if (obj.length === +obj.length) results.length = obj.length;
+ return results;
+ }; |
| Reduce builds up a single result from a list of values, aka | _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduce && obj.reduce === nativeReduce) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);
+ }
+ each(obj, function(value, index, list) {
+ if (!initial) {
+ memo = value;
+ initial = true;
+ } else {
+ memo = iterator.call(context, memo, value, index, list);
+ }
+ });
+ if (!initial) throw new TypeError('Reduce of empty array with no initial value');
+ return memo;
+ }; |
| The right-associative version of reduce, also known as | _.reduceRight = _.foldr = function(obj, iterator, memo, context) {
+ var initial = arguments.length > 2;
+ if (obj == null) obj = [];
+ if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {
+ if (context) iterator = _.bind(iterator, context);
+ return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
+ }
+ var reversed = _.toArray(obj).reverse();
+ if (context && !initial) iterator = _.bind(iterator, context);
+ return initial ? _.reduce(reversed, iterator, memo, context) : _.reduce(reversed, iterator);
+ }; |
| Return the first value which passes a truth test. Aliased as | _.find = _.detect = function(obj, iterator, context) {
+ var result;
+ any(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) {
+ result = value;
+ return true;
+ }
+ });
+ return result;
+ }; |
| Return all the elements that pass a truth test.
+Delegates to ECMAScript 5's native | _.filter = _.select = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);
+ each(obj, function(value, index, list) {
+ if (iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ }; |
| Return all the elements for which a truth test fails. | _.reject = function(obj, iterator, context) {
+ var results = [];
+ if (obj == null) return results;
+ each(obj, function(value, index, list) {
+ if (!iterator.call(context, value, index, list)) results[results.length] = value;
+ });
+ return results;
+ }; |
| Determine whether all of the elements match a truth test.
+Delegates to ECMAScript 5's native | _.every = _.all = function(obj, iterator, context) {
+ var result = true;
+ if (obj == null) return result;
+ if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
+ each(obj, function(value, index, list) {
+ if (!(result = result && iterator.call(context, value, index, list))) return breaker;
+ });
+ return result;
+ }; |
| Determine if at least one element in the object matches a truth test.
+Delegates to ECMAScript 5's native | var any = _.some = _.any = function(obj, iterator, context) {
+ iterator || (iterator = _.identity);
+ var result = false;
+ if (obj == null) return result;
+ if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);
+ each(obj, function(value, index, list) {
+ if (result || (result = iterator.call(context, value, index, list))) return breaker;
+ });
+ return !!result;
+ }; |
| Determine if a given value is included in the array or object using | _.include = _.contains = function(obj, target) {
+ var found = false;
+ if (obj == null) return found;
+ if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
+ found = any(obj, function(value) {
+ return value === target;
+ });
+ return found;
+ }; |
| Invoke a method (with arguments) on every item in a collection. | _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ return _.map(obj, function(value) {
+ return (_.isFunction(method) ? method || value : value[method]).apply(value, args);
+ });
+ }; |
| Convenience version of a common use case of | _.pluck = function(obj, key) {
+ return _.map(obj, function(value){ return value[key]; });
+ }; |
| Return the maximum element or (element-based computation). | _.max = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.max.apply(Math, obj);
+ if (!iterator && _.isEmpty(obj)) return -Infinity;
+ var result = {computed : -Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed >= result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ }; |
| Return the minimum element (or element-based computation). | _.min = function(obj, iterator, context) {
+ if (!iterator && _.isArray(obj)) return Math.min.apply(Math, obj);
+ if (!iterator && _.isEmpty(obj)) return Infinity;
+ var result = {computed : Infinity};
+ each(obj, function(value, index, list) {
+ var computed = iterator ? iterator.call(context, value, index, list) : value;
+ computed < result.computed && (result = {value : value, computed : computed});
+ });
+ return result.value;
+ }; |
| Shuffle an array. | _.shuffle = function(obj) {
+ var shuffled = [], rand;
+ each(obj, function(value, index, list) {
+ if (index == 0) {
+ shuffled[0] = value;
+ } else {
+ rand = Math.floor(Math.random() * (index + 1));
+ shuffled[index] = shuffled[rand];
+ shuffled[rand] = value;
+ }
+ });
+ return shuffled;
+ }; |
| Sort the object's values by a criterion produced by an iterator. | _.sortBy = function(obj, iterator, context) {
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value : value,
+ criteria : iterator.call(context, value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria, b = right.criteria;
+ return a < b ? -1 : a > b ? 1 : 0;
+ }), 'value');
+ }; |
| Groups the object's values by a criterion. Pass either a string attribute +to group by, or a function that returns the criterion. | _.groupBy = function(obj, val) {
+ var result = {};
+ var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; };
+ each(obj, function(value, index) {
+ var key = iterator(value, index);
+ (result[key] || (result[key] = [])).push(value);
+ });
+ return result;
+ }; |
| Use a comparator function to figure out at what index an object should +be inserted so as to maintain order. Uses binary search. | _.sortedIndex = function(array, obj, iterator) {
+ iterator || (iterator = _.identity);
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = (low + high) >> 1;
+ iterator(array[mid]) < iterator(obj) ? low = mid + 1 : high = mid;
+ }
+ return low;
+ }; |
| Safely convert anything iterable into a real, live array. | _.toArray = function(iterable) {
+ if (!iterable) return [];
+ if (iterable.toArray) return iterable.toArray();
+ if (_.isArray(iterable)) return slice.call(iterable);
+ if (_.isArguments(iterable)) return slice.call(iterable);
+ return _.values(iterable);
+ }; |
| Return the number of elements in an object. | _.size = function(obj) {
+ return _.toArray(obj).length;
+ }; |
Array Functions | |
| Get the first element of an array. Passing n will return the first N
+values in the array. Aliased as | _.first = _.head = function(array, n, guard) {
+ return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
+ }; |
| Returns everything but the last entry of the array. Especcialy useful on
+the arguments object. Passing n will return all the values in
+the array, excluding the last N. The guard check allows it to work with
+ | _.initial = function(array, n, guard) {
+ return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));
+ }; |
| Get the last element of an array. Passing n will return the last N
+values in the array. The guard check allows it to work with | _.last = function(array, n, guard) {
+ if ((n != null) && !guard) {
+ return slice.call(array, Math.max(array.length - n, 0));
+ } else {
+ return array[array.length - 1];
+ }
+ }; |
| Returns everything but the first entry of the array. Aliased as | _.rest = _.tail = function(array, index, guard) {
+ return slice.call(array, (index == null) || guard ? 1 : index);
+ }; |
| Trim out all falsy values from an array. | _.compact = function(array) {
+ return _.filter(array, function(value){ return !!value; });
+ }; |
| Return a completely flattened version of an array. | _.flatten = function(array, shallow) {
+ return _.reduce(array, function(memo, value) {
+ if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
+ memo[memo.length] = value;
+ return memo;
+ }, []);
+ }; |
| Return a version of the array that does not contain the specified value(s). | _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ }; |
| Produce a duplicate-free version of the array. If the array has already
+been sorted, you have the option of using a faster algorithm.
+Aliased as | _.uniq = _.unique = function(array, isSorted, iterator) {
+ var initial = iterator ? _.map(array, iterator) : array;
+ var result = [];
+ _.reduce(initial, function(memo, el, i) {
+ if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) {
+ memo[memo.length] = el;
+ result[result.length] = array[i];
+ }
+ return memo;
+ }, []);
+ return result;
+ }; |
| Produce an array that contains the union: each distinct element from all of +the passed-in arrays. | _.union = function() {
+ return _.uniq(_.flatten(arguments, true));
+ }; |
| Produce an array that contains every item shared between all the +passed-in arrays. (Aliased as "intersect" for back-compat.) | _.intersection = _.intersect = function(array) {
+ var rest = slice.call(arguments, 1);
+ return _.filter(_.uniq(array), function(item) {
+ return _.every(rest, function(other) {
+ return _.indexOf(other, item) >= 0;
+ });
+ });
+ }; |
| Take the difference between one array and a number of other arrays. +Only the elements present in just the first array will remain. | _.difference = function(array) {
+ var rest = _.flatten(slice.call(arguments, 1));
+ return _.filter(array, function(value){ return !_.include(rest, value); });
+ }; |
| Zip together multiple lists into a single array -- elements that share +an index go together. | _.zip = function() {
+ var args = slice.call(arguments);
+ var length = _.max(_.pluck(args, 'length'));
+ var results = new Array(length);
+ for (var i = 0; i < length; i++) results[i] = _.pluck(args, "" + i);
+ return results;
+ }; |
| If the browser doesn't supply us with indexOf (I'm looking at you, MSIE),
+we need this function. Return the position of the first occurrence of an
+item in an array, or -1 if the item is not included in the array.
+Delegates to ECMAScript 5's native | _.indexOf = function(array, item, isSorted) {
+ if (array == null) return -1;
+ var i, l;
+ if (isSorted) {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
+ if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
+ for (i = 0, l = array.length; i < l; i++) if (i in array && array[i] === item) return i;
+ return -1;
+ }; |
| Delegates to ECMAScript 5's native | _.lastIndexOf = function(array, item) {
+ if (array == null) return -1;
+ if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) return array.lastIndexOf(item);
+ var i = array.length;
+ while (i--) if (i in array && array[i] === item) return i;
+ return -1;
+ }; |
| Generate an integer Array containing an arithmetic progression. A port of
+the native Python | _.range = function(start, stop, step) {
+ if (arguments.length <= 1) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = arguments[2] || 1;
+
+ var len = Math.max(Math.ceil((stop - start) / step), 0);
+ var idx = 0;
+ var range = new Array(len);
+
+ while(idx < len) {
+ range[idx++] = start;
+ start += step;
+ }
+
+ return range;
+ }; |
Function (ahem) Functions | |
| Reusable constructor function for prototype setting. | var ctor = function(){}; |
| Create a function bound to a given object (assigning | _.bind = function bind(func, context) {
+ var bound, args;
+ if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ if (!_.isFunction(func)) throw new TypeError;
+ args = slice.call(arguments, 2);
+ return bound = function() {
+ if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments)));
+ ctor.prototype = func.prototype;
+ var self = new ctor;
+ var result = func.apply(self, args.concat(slice.call(arguments)));
+ if (Object(result) === result) return result;
+ return self;
+ };
+ }; |
| Bind all of an object's methods to that object. Useful for ensuring that +all callbacks defined on an object belong to it. | _.bindAll = function(obj) {
+ var funcs = slice.call(arguments, 1);
+ if (funcs.length == 0) funcs = _.functions(obj);
+ each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
+ return obj;
+ }; |
| Memoize an expensive function by storing its results. | _.memoize = function(func, hasher) {
+ var memo = {};
+ hasher || (hasher = _.identity);
+ return function() {
+ var key = hasher.apply(this, arguments);
+ return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));
+ };
+ }; |
| Delays a function for the given number of milliseconds, and then calls +it with the arguments supplied. | _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){ return func.apply(func, args); }, wait);
+ }; |
| Defers a function, scheduling it to run after the current call stack has +cleared. | _.defer = function(func) {
+ return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));
+ }; |
| Returns a function, that, when invoked, will only be triggered at most once +during a given window of time. | _.throttle = function(func, wait) {
+ var context, args, timeout, throttling, more;
+ var whenDone = _.debounce(function(){ more = throttling = false; }, wait);
+ return function() {
+ context = this; args = arguments;
+ var later = function() {
+ timeout = null;
+ if (more) func.apply(context, args);
+ whenDone();
+ };
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (throttling) {
+ more = true;
+ } else {
+ func.apply(context, args);
+ }
+ whenDone();
+ throttling = true;
+ };
+ }; |
| Returns a function, that, as long as it continues to be invoked, will not +be triggered. The function will be called after it stops being called for +N milliseconds. | _.debounce = function(func, wait) {
+ var timeout;
+ return function() {
+ var context = this, args = arguments;
+ var later = function() {
+ timeout = null;
+ func.apply(context, args);
+ };
+ clearTimeout(timeout);
+ timeout = setTimeout(later, wait);
+ };
+ }; |
| Returns a function that will be executed at most one time, no matter how +often you call it. Useful for lazy initialization. | _.once = function(func) {
+ var ran = false, memo;
+ return function() {
+ if (ran) return memo;
+ ran = true;
+ return memo = func.apply(this, arguments);
+ };
+ }; |
| Returns the first function passed as an argument to the second, +allowing you to adjust arguments, run code before and after, and +conditionally execute the original function. | _.wrap = function(func, wrapper) {
+ return function() {
+ var args = [func].concat(slice.call(arguments, 0));
+ return wrapper.apply(this, args);
+ };
+ }; |
| Returns a function that is the composition of a list of functions, each +consuming the return value of the function that follows. | _.compose = function() {
+ var funcs = arguments;
+ return function() {
+ var args = arguments;
+ for (var i = funcs.length - 1; i >= 0; i--) {
+ args = [funcs[i].apply(this, args)];
+ }
+ return args[0];
+ };
+ }; |
| Returns a function that will only be executed after being called N times. | _.after = function(times, func) {
+ if (times <= 0) return func();
+ return function() {
+ if (--times < 1) { return func.apply(this, arguments); }
+ };
+ }; |
Object Functions | |
| Retrieve the names of an object's properties.
+Delegates to ECMAScript 5's native | _.keys = nativeKeys || function(obj) {
+ if (obj !== Object(obj)) throw new TypeError('Invalid object');
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;
+ return keys;
+ }; |
| Retrieve the values of an object's properties. | _.values = function(obj) {
+ return _.map(obj, _.identity);
+ }; |
| Return a sorted list of the function names available on the object.
+Aliased as | _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ }; |
| Extend a given object with all the properties in passed-in object(s). | _.extend = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ }; |
| Fill in a given object with default properties. | _.defaults = function(obj) {
+ each(slice.call(arguments, 1), function(source) {
+ for (var prop in source) {
+ if (obj[prop] == null) obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ }; |
| Create a (shallow-cloned) duplicate of an object. | _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+ }; |
| Invokes interceptor with the obj, and then returns obj. +The primary purpose of this method is to "tap into" a method chain, in +order to perform operations on intermediate results within the chain. | _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ }; |
| Internal recursive comparison function. | function eq(a, b, stack) { |
| Identical objects are equal. | if (a === b) return a !== 0 || 1 / a == 1 / b; |
| A strict comparison is necessary because | 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 | if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
+ if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a); |
| Compare | var className = toString.call(a);
+ if (className != toString.call(b)) return false;
+ switch (className) { |
| Strings, numbers, dates, and booleans are compared by value. | case '[object String]': |
| Primitives and their corresponding object wrappers are equivalent; thus, | return a == String(b);
+ case '[object Number]': |
|
| return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);
+ case '[object Date]':
+ case '[object Boolean]': |
| Coerce dates and booleans to numeric primitive values. Dates are compared by their
+millisecond representations. Note that invalid dates with millisecond representations
+of | return +a == +b; |
| RegExps are compared by their source patterns and flags. | case '[object RegExp]':
+ return a.source == b.source &&
+ a.global == b.global &&
+ a.multiline == b.multiline &&
+ a.ignoreCase == b.ignoreCase;
+ }
+ if (typeof a != 'object' || typeof b != 'object') return false; |
| Assume equality for cyclic structures. The algorithm for detecting cyclic
+structures is adapted from ES 5.1 section 15.12.3, abstract operation | 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;
+ } |
| Add the first object to the stack of traversed objects. | stack.push(a);
+ var size = 0, result = true; |
| Recursively compare objects and arrays. | if (className == '[object Array]') { |
| Compare array 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--) { |
| Ensure commutative equality for sparse arrays. | if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
+ }
+ }
+ } 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 key in a) {
+ if (_.has(a, key)) { |
| Count the expected number of properties. | size++; |
| Deep compare each member. | if (!(result = _.has(b, key) && eq(a[key], b[key], stack))) break;
+ }
+ } |
| Ensure that both objects contain the same number of properties. | if (result) {
+ for (key in b) {
+ if (_.has(b, key) && !(size--)) break;
+ }
+ result = !size;
+ }
+ } |
| Remove the first object from the stack of traversed objects. | stack.pop();
+ return result;
+ } |
| Perform a deep comparison to check if two objects are equal. | _.isEqual = function(a, b) {
+ return eq(a, b, []);
+ }; |
| Is a given array, string, or object empty? +An "empty" object has no enumerable own-properties. | _.isEmpty = function(obj) {
+ if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;
+ for (var key in obj) if (_.has(obj, key)) return false;
+ return true;
+ }; |
| Is a given value a DOM element? | _.isElement = function(obj) {
+ return !!(obj && obj.nodeType == 1);
+ }; |
| Is a given value an array? +Delegates to ECMA5's native Array.isArray | _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) == '[object Array]';
+ }; |
| Is a given variable an object? | _.isObject = function(obj) {
+ return obj === Object(obj);
+ }; |
| Is a given variable an arguments object? | _.isArguments = function(obj) {
+ return toString.call(obj) == '[object Arguments]';
+ };
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return !!(obj && _.has(obj, 'callee'));
+ };
+ } |
| Is a given value a function? | _.isFunction = function(obj) {
+ return toString.call(obj) == '[object Function]';
+ }; |
| Is a given value a string? | _.isString = function(obj) {
+ return toString.call(obj) == '[object String]';
+ }; |
| Is a given value a number? | _.isNumber = function(obj) {
+ return toString.call(obj) == '[object Number]';
+ }; |
| Is the given value | _.isNaN = function(obj) { |
|
| return obj !== obj;
+ }; |
| Is a given value a boolean? | _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
+ }; |
| Is a given value a date? | _.isDate = function(obj) {
+ return toString.call(obj) == '[object Date]';
+ }; |
| Is the given value a regular expression? | _.isRegExp = function(obj) {
+ return toString.call(obj) == '[object RegExp]';
+ }; |
| Is a given value equal to null? | _.isNull = function(obj) {
+ return obj === null;
+ }; |
| Is a given variable undefined? | _.isUndefined = function(obj) {
+ return obj === void 0;
+ }; |
| Has own property? | _.has = function(obj, key) {
+ return hasOwnProperty.call(obj, key);
+ }; |
Utility Functions | |
| Run Underscore.js in noConflict mode, returning the | _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ }; |
| Keep the identity function around for default iterators. | _.identity = function(value) {
+ return value;
+ }; |
| Run a function n times. | _.times = function (n, iterator, context) {
+ for (var i = 0; i < n; i++) iterator.call(context, i);
+ }; |
| Escape a string for HTML interpolation. | _.escape = function(string) {
+ return (''+string).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''').replace(/\//g,'/');
+ }; |
| Add your own custom functions to the Underscore object, ensuring that +they're correctly added to the OOP wrapper as well. | _.mixin = function(obj) {
+ each(_.functions(obj), function(name){
+ addToWrapper(name, _[name] = obj[name]);
+ });
+ }; |
| Generate a unique integer id (unique within the entire client session). +Useful for temporary DOM ids. | var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = idCounter++;
+ return prefix ? prefix + id : id;
+ }; |
| By default, Underscore uses ERB-style template delimiters, change the +following template settings to use alternative delimiters. | _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ }; |
| When customizing | var noMatch = /.^/; |
| Within an interpolation, evaluation, or escaping, remove HTML escaping +that had been previously added. | var unescape = function(code) {
+ return code.replace(/\\\\/g, '\\').replace(/\\'/g, "'");
+ }; |
| JavaScript micro-templating, similar to John Resig's implementation. +Underscore templating handles arbitrary delimiters, preserves whitespace, +and correctly escapes quotes within interpolated code. | _.template = function(str, data) {
+ var c = _.templateSettings;
+ var tmpl = 'var __p=[],print=function(){__p.push.apply(__p,arguments);};' +
+ 'with(obj||{}){__p.push(\'' +
+ str.replace(/\\/g, '\\\\')
+ .replace(/'/g, "\\'")
+ .replace(c.escape || noMatch, function(match, code) {
+ return "',_.escape(" + unescape(code) + "),'";
+ })
+ .replace(c.interpolate || noMatch, function(match, code) {
+ return "'," + unescape(code) + ",'";
+ })
+ .replace(c.evaluate || noMatch, function(match, code) {
+ return "');" + unescape(code).replace(/[\r\n\t]/g, ' ') + ";__p.push('";
+ })
+ .replace(/\r/g, '\\r')
+ .replace(/\n/g, '\\n')
+ .replace(/\t/g, '\\t')
+ + "');}return __p.join('');";
+ var func = new Function('obj', '_', tmpl);
+ if (data) return func(data, _);
+ return function(data) {
+ return func.call(this, data, _);
+ };
+ }; |
| Add a "chain" function, which will delegate to the wrapper. | _.chain = function(obj) {
+ return _(obj).chain();
+ }; |
The OOP Wrapper | |
| If Underscore is called as a function, it returns a wrapped object that +can be used OO-style. This wrapper holds altered versions of all the +underscore functions. Wrapped objects may be chained. | var wrapper = function(obj) { this._wrapped = obj; }; |
| Expose | _.prototype = wrapper.prototype; |
| Helper function to continue chaining intermediate results. | var result = function(obj, chain) {
+ return chain ? _(obj).chain() : obj;
+ }; |
| A method to easily add functions to the OOP wrapper. | var addToWrapper = function(name, func) {
+ wrapper.prototype[name] = function() {
+ var args = slice.call(arguments);
+ unshift.call(args, this._wrapped);
+ return result(func.apply(_, args), this._chain);
+ };
+ }; |
| Add all of the Underscore functions to the wrapper object. | _.mixin(_); |
| Add all mutator Array functions to the wrapper. | each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ var wrapped = this._wrapped;
+ method.apply(wrapped, arguments);
+ var length = wrapped.length;
+ if ((name == 'shift' || name == 'splice') && length === 0) delete wrapped[0];
+ return result(wrapped, this._chain);
+ };
+ }); |
| Add all accessor Array functions to the wrapper. | each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ wrapper.prototype[name] = function() {
+ return result(method.apply(this._wrapped, arguments), this._chain);
+ };
+ }); |
| Start chaining a wrapped Underscore object. | wrapper.prototype.chain = function() {
+ this._chain = true;
+ return this;
+ }; |
| Extracts the result from a wrapped and chained object. | wrapper.prototype.value = function() {
+ return this._wrapped;
+ };
+
+}).call(this);
+
+ |
+
+
+ Underscore is a + utility-belt library for JavaScript that provides a lot of the + functional programming support that you would expect in + Prototype.js + (or Ruby), + but without extending any of the built-in JavaScript objects. It's the + tie to go along with jQuery's tux, + and Backbone.js's suspenders. +
+ ++ Underscore provides 60-odd functions that support both the usual + functional suspects: map, select, invoke — + as well as more specialized helpers: function binding, javascript + templating, deep equality testing, and so on. It delegates to built-in + functions, if present, so modern browsers will use the + native implementations of forEach, map, reduce, + filter, every, some and indexOf. +
+ ++ A complete Test & Benchmark Suite + is included for your perusal. +
+ ++ You may also read through the annotated source code. +
+ ++ The project is + hosted on GitHub. + You can report bugs and discuss features on the + issues page, + on Freenode in the #documentcloud channel, + or send tweets to @documentcloud. +
+ ++ Underscore is an open-source component of DocumentCloud. +
+ +| Development Version (1.3.1) | +34kb, Uncompressed with Comments | +
| Production Version (1.3.1) | +< 4kb, Minified and Gzipped | +
+ each_.each(list, iterator, [context])
+ Alias: forEach
+
+ Iterates over a list of elements, yielding each in turn to an iterator
+ function. The iterator is bound to the context object, if one is
+ passed. Each invocation of iterator is called with three arguments:
+ (element, index, list). If list is a JavaScript object, iterator's
+ arguments will be (value, key, list). Delegates to the native
+ forEach function if it exists.
+
+_.each([1, 2, 3], function(num){ alert(num); });
+=> alerts each number in turn...
+_.each({one : 1, two : 2, three : 3}, function(num, key){ alert(num); });
+=> alerts each number in turn...
+
+
+ map_.map(list, iterator, [context])
+ Alias: collect
+
+ Produces a new array of values by mapping each value in list
+ through a transformation function (iterator). If the native map method
+ exists, it will be used instead. If list is a JavaScript object,
+ iterator's arguments will be (value, key, list).
+
+_.map([1, 2, 3], function(num){ return num * 3; });
+=> [3, 6, 9]
+_.map({one : 1, two : 2, three : 3}, function(num, key){ return num * 3; });
+=> [3, 6, 9]
+
+
+ reduce_.reduce(list, iterator, memo, [context])
+ Aliases: inject, foldl
+
+ Also known as inject and foldl, reduce boils down a
+ list of values into a single value. Memo is the initial state
+ of the reduction, and each successive step of it should be returned by
+ iterator.
+
+var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num; }, 0);
+=> 6
+
+
+
+ reduceRight_.reduceRight(list, iterator, memo, [context])
+ Alias: foldr
+
+ The right-associative version of reduce. Delegates to the
+ JavaScript 1.8 version of reduceRight, if it exists. Foldr
+ is not as useful in JavaScript as it would be in a language with lazy
+ evaluation.
+
+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]
+
+
+
+ find_.find(list, iterator, [context])
+ Alias: detect
+
+ Looks through each value in the list, returning the first one that
+ passes a truth test (iterator). The function returns as
+ soon as it finds an acceptable element, and doesn't traverse the
+ entire list.
+
+var even = _.find([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
+=> 2
+
+
+
+ filter_.filter(list, iterator, [context])
+ Alias: select
+
+ Looks through each value in the list, returning an array of all
+ the values that pass a truth test (iterator). Delegates to the
+ native filter method, if it exists.
+
+var evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
+=> [2, 4, 6]
+
+
+
+ reject_.reject(list, iterator, [context])
+
+ Returns the values in list without the elements that the truth
+ test (iterator) passes. The opposite of filter.
+
+var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
+=> [1, 3, 5]
+
+
+
+ all_.all(list, iterator, [context])
+ Alias: every
+
+ Returns true if all of the values in the list pass the iterator
+ truth test. Delegates to the native method every, if present.
+
+_.all([true, 1, null, 'yes'], _.identity); +=> false ++ +
+ any_.any(list, [iterator], [context])
+ Alias: some
+
+ Returns true if any of the values in the list pass the
+ iterator truth test. Short-circuits and stops traversing the list
+ if a true element is found. Delegates to the native method some,
+ if present.
+
+_.any([null, 0, 'yes', false]); +=> true ++ +
+ include_.include(list, value)
+ Alias: contains
+
+ Returns true if the value is present in the list, using
+ === to test equality. Uses indexOf internally, if list
+ is an Array.
+
+_.include([1, 2, 3], 3); +=> true ++ +
+ invoke_.invoke(list, methodName, [*arguments])
+
+ Calls the method named by methodName on each value in the list.
+ Any extra arguments passed to invoke will be forwarded on to the
+ method invocation.
+
+_.invoke([[5, 1, 7], [3, 2, 1]], 'sort'); +=> [[1, 5, 7], [1, 2, 3]] ++ +
+ pluck_.pluck(list, propertyName)
+
+ A convenient version of what is perhaps the most common use-case for
+ map: extracting a list of property values.
+
+var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
+_.pluck(stooges, 'name');
+=> ["moe", "larry", "curly"]
+
+
+
+ max_.max(list, [iterator], [context])
+
+ Returns the maximum value in list. If iterator is passed,
+ it will be used on each value to generate the criterion by which the
+ value is ranked.
+
+var stooges = [{name : 'moe', age : 40}, {name : 'larry', age : 50}, {name : 'curly', age : 60}];
+_.max(stooges, function(stooge){ return stooge.age; });
+=> {name : 'curly', age : 60};
+
+
+
+ min_.min(list, [iterator], [context])
+
+ Returns the minimum value in list. If iterator is passed,
+ it will be used on each value to generate the criterion by which the
+ value is ranked.
+
+var numbers = [10, 5, 100, 2, 1000]; +_.min(numbers); +=> 2 ++ +
+ sortBy_.sortBy(list, iterator, [context])
+
+ Returns a sorted copy of list, ranked in ascending order by the
+ results of running each value through iterator.
+
+_.sortBy([1, 2, 3, 4, 5, 6], function(num){ return Math.sin(num); });
+=> [5, 4, 6, 3, 1, 2]
+
+
+
+ groupBy_.groupBy(list, iterator)
+
+ Splits a collection into sets, grouped by the result of running each
+ value through iterator. If iterator is a string instead of
+ a function, groups by the property named by iterator on each of
+ the values.
+
+_.groupBy([1.3, 2.1, 2.4], function(num){ return Math.floor(num); });
+=> {1: [1.3], 2: [2.1, 2.4]}
+
+_.groupBy(['one', 'two', 'three'], 'length');
+=> {3: ["one", "two"], 5: ["three"]}
+
+
+
+ sortedIndex_.sortedIndex(list, value, [iterator])
+
+ Uses a binary search to determine the index at which the value
+ should be inserted into the list in order to maintain the list's
+ sorted order. If an iterator is passed, it will be used to compute
+ the sort ranking of each value.
+
+_.sortedIndex([10, 20, 30, 40, 50], 35); +=> 3 ++ +
+ shuffle_.shuffle(list)
+
+ Returns a shuffled copy of the list, using a version of the
+ Fisher-Yates shuffle.
+
+_.shuffle([1, 2, 3, 4, 5, 6]); +=> [4, 1, 6, 3, 5, 2] ++ +
+ toArray_.toArray(list)
+
+ Converts the list (anything that can be iterated over), into a
+ real Array. Useful for transmuting the arguments object.
+
+(function(){ return _.toArray(arguments).slice(1); })(1, 2, 3, 4);
+=> [2, 3, 4]
+
+
+
+ size_.size(list)
+
+ Return the number of values in the list.
+
+_.size({one : 1, two : 2, three : 3});
+=> 3
+
+
+ + Note: All array functions will also work on the arguments object. +
+ +
+ first_.first(array, [n])
+ Alias: head
+
+ Returns the first element of an array. Passing n will
+ return the first n elements of the array.
+
+_.first([5, 4, 3, 2, 1]); +=> 5 ++ +
+ initial_.initial(array, [n])
+
+ Returns everything but the last entry of the array. Especially useful on
+ the arguments object. Pass n to exclude the last n elements
+ from the result.
+
+_.initial([5, 4, 3, 2, 1]); +=> [5, 4, 3, 2] ++ +
+ last_.last(array, [n])
+
+ Returns the last element of an array. Passing n will return
+ the last n elements of the array.
+
+_.last([5, 4, 3, 2, 1]); +=> 1 ++ +
+ rest_.rest(array, [index])
+ Alias: tail
+
+ Returns the rest of the elements in an array. Pass an index
+ to return the values of the array from that index onward.
+
+_.rest([5, 4, 3, 2, 1]); +=> [4, 3, 2, 1] ++ +
+ compact_.compact(array)
+
+ Returns a copy of the array with all falsy values removed.
+ In JavaScript, false, null, 0, "",
+ undefined and NaN are all falsy.
+
+_.compact([0, 1, false, 2, '', 3]); +=> [1, 2, 3] ++ +
+ flatten_.flatten(array, [shallow])
+
+ Flattens a nested array (the nesting can be to any depth). If you
+ pass shallow, the array will only be flattened a single level.
+
+_.flatten([1, [2], [3, [[4]]]]); +=> [1, 2, 3, 4]; + +_.flatten([1, [2], [3, [[4]]]], true); +=> [1, 2, 3, [[4]]]; ++ +
+ without_.without(array, [*values])
+
+ Returns a copy of the array with all instances of the values
+ removed. === is used for the equality test.
+
+_.without([1, 2, 1, 0, 3, 1, 4], 0, 1); +=> [2, 3, 4] ++ +
+ union_.union(*arrays)
+
+ Computes the union of the passed-in arrays: the list of unique items,
+ in order, that are present in one or more of the arrays.
+
+_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); +=> [1, 2, 3, 101, 10] ++ +
+ intersection_.intersection(*arrays)
+
+ Computes the list of values that are the intersection of all the arrays.
+ Each value in the result is present in each of the arrays.
+
+_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]); +=> [1, 2] ++ +
+ difference_.difference(array, *others)
+
+ Similar to without, but returns the values from array that
+ are not present in the other arrays.
+
+_.difference([1, 2, 3, 4, 5], [5, 2, 10]); +=> [1, 3, 4] ++ +
+ uniq_.uniq(array, [isSorted], [iterator])
+ Alias: unique
+
+ Produces a duplicate-free version of the array, using === to test
+ object equality. If you know in advance that the array is sorted,
+ passing true for isSorted will run a much faster algorithm.
+ If you want to compute unique items based on a transformation, pass an
+ iterator function.
+
+_.uniq([1, 2, 1, 3, 1, 4]); +=> [1, 2, 3, 4] ++ +
+ zip_.zip(*arrays)
+
+ Merges together the values of each of the arrays with the
+ values at the corresponding position. Useful when you have separate
+ data sources that are coordinated through matching array indexes.
+ If you're working with a matrix of nested arrays, zip.apply
+ can transpose the matrix in a similar fashion.
+
+_.zip(['moe', 'larry', 'curly'], [30, 40, 50], [true, false, false]); +=> [["moe", 30, true], ["larry", 40, false], ["curly", 50, false]] ++ +
+ indexOf_.indexOf(array, value, [isSorted])
+
+ Returns the index at which value can be found in the array,
+ or -1 if value is not present in the array. Uses the native
+ indexOf function unless it's missing. If you're working with a
+ large array, and you know that the array is already sorted, pass true
+ for isSorted to use a faster binary search.
+
+_.indexOf([1, 2, 3], 2); +=> 1 ++ +
+ lastIndexOf_.lastIndexOf(array, value)
+
+ Returns the index of the last occurrence of value in the array,
+ or -1 if value is not present. Uses the native lastIndexOf
+ function if possible.
+
+_.lastIndexOf([1, 2, 3, 1, 2, 3], 2); +=> 4 ++ +
+ range_.range([start], stop, [step])
+
+ A function to create flexibly-numbered lists of integers, handy for
+ each and map loops. start, if omitted, defaults
+ to 0; step defaults to 1. Returns a list of integers
+ from start to stop, incremented (or decremented) by step,
+ exclusive.
+
+_.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); +=> [] ++ +
+ bind_.bind(function, object, [*arguments])
+
+ Bind a function to an object, meaning that whenever
+ the function is called, the value of this will be the object.
+ Optionally, bind arguments to the function to pre-fill them,
+ also known as partial application.
+
+var func = function(greeting){ return greeting + ': ' + this.name };
+func = _.bind(func, {name : 'moe'}, 'hi');
+func();
+=> 'hi: moe'
+
+
+
+ bindAll_.bindAll(object, [*methodNames])
+
+ Binds a number of methods on the object, specified by
+ methodNames, to be run in the context of that object whenever they
+ are invoked. Very handy for binding functions that are going to be used
+ as event handlers, which would otherwise be invoked with a fairly useless
+ this. If no methodNames are provided, all of the object's
+ function properties will be bound to it.
+
+var buttonView = {
+ label : 'underscore',
+ onClick : function(){ alert('clicked: ' + this.label); },
+ onHover : function(){ console.log('hovering: ' + this.label); }
+};
+_.bindAll(buttonView);
+jQuery('#underscore_button').bind('click', buttonView.onClick);
+=> When the button is clicked, this.label will have the correct value...
+
+
+
+ memoize_.memoize(function, [hashFunction])
+
+ Memoizes a given function by caching the computed result. Useful
+ for speeding up slow-running computations. If passed an optional
+ hashFunction, it will be used to compute the hash key for storing
+ the result, based on the arguments to the original function. The default
+ hashFunction just uses the first argument to the memoized function
+ as the key.
+
+var fibonacci = _.memoize(function(n) {
+ return n < 2 ? n : fibonacci(n - 1) + fibonacci(n - 2);
+});
+
+
+
+ delay_.delay(function, wait, [*arguments])
+
+ Much like setTimeout, invokes function after wait
+ milliseconds. If you pass the optional arguments, they will be
+ forwarded on to the function when it is invoked.
+
+var log = _.bind(console.log, console); +_.delay(log, 1000, 'logged later'); +=> 'logged later' // Appears after one second. ++ +
+ defer_.defer(function)
+
+ Defers invoking the function until the current call stack has cleared,
+ similar to using setTimeout with a delay of 0. Useful for performing
+ expensive computations or HTML rendering in chunks without blocking the UI thread
+ from updating.
+
+_.defer(function(){ alert('deferred'); });
+// Returns from the function before the alert runs.
+
+
+
+ throttle_.throttle(function, wait)
+
+ Creates and returns a new, throttled version of the passed function,
+ that, when invoked repeatedly, will only actually call the original function
+ at most once per every wait
+ milliseconds. Useful for rate-limiting events that occur faster than you
+ can keep up with.
+
+var throttled = _.throttle(updatePosition, 100); +$(window).scroll(throttled); ++ +
+ debounce_.debounce(function, wait)
+
+ Creates and returns a new debounced version of the passed function that
+ will postpone its execution until after
+ wait milliseconds have elapsed since the last time it
+ was invoked. Useful for implementing behavior that should only happen
+ after the input has stopped arriving. For example: rendering a
+ preview of a Markdown comment, recalculating a layout after the window
+ has stopped being resized, and so on.
+
+var lazyLayout = _.debounce(calculateLayout, 300); +$(window).resize(lazyLayout); ++ +
+ once_.once(function)
+
+ Creates a version of the function that can only be called one time.
+ Repeated calls to the modified function will have no effect, returning
+ the value from the original call. Useful for initialization functions,
+ instead of having to set a boolean flag and then check it later.
+
+var initialize = _.once(createApplication); +initialize(); +initialize(); +// Application is only created once. ++ +
+ after_.after(count, function)
+
+ Creates a version of the function that will only be run after first
+ being called count times. Useful for grouping asynchronous responses,
+ where you want to be sure that all the async calls have finished, before
+ proceeding.
+
+var renderNotes = _.after(notes.length, render);
+_.each(notes, function(note) {
+ note.asyncSave({success: renderNotes});
+});
+// renderNotes is run once, after all notes have saved.
+
+
+
+ wrap_.wrap(function, wrapper)
+
+ Wraps the first function inside of the wrapper function,
+ passing it as the first argument. This allows the wrapper to
+ execute code before and after the function runs, adjust the arguments,
+ and execute it conditionally.
+
+var hello = function(name) { return "hello: " + name; };
+hello = _.wrap(hello, function(func) {
+ return "before, " + func("moe") + ", after";
+});
+hello();
+=> 'before, hello: moe, after'
+
+
+
+ compose_.compose(*functions)
+
+ Returns the composition of a list of 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())).
+
+var greet = function(name){ return "hi: " + name; };
+var exclaim = function(statement){ return statement + "!"; };
+var welcome = _.compose(exclaim, greet);
+welcome('moe');
+=> 'hi: moe!'
+
+
+
+ keys_.keys(object)
+
+ Retrieve all the names of the object's properties.
+
+_.keys({one : 1, two : 2, three : 3});
+=> ["one", "two", "three"]
+
+
+
+ values_.values(object)
+
+ Return all of the values of the object's properties.
+
+_.values({one : 1, two : 2, three : 3});
+=> [1, 2, 3]
+
+
+
+ functions_.functions(object)
+ Alias: methods
+
+ Returns a sorted list of the names of every method in an object —
+ that is to say, the name of every function property of the object.
+
+_.functions(_); +=> ["all", "any", "bind", "bindAll", "clone", "compact", "compose" ... ++ +
+ extend_.extend(destination, *sources)
+
+ Copy all of the properties in the source objects over to the
+ destination object, and return the destination object.
+ It's in-order, so the last source will override properties of the same
+ name in previous arguments.
+
+_.extend({name : 'moe'}, {age : 50});
+=> {name : 'moe', age : 50}
+
+
+
+ pick_.pick(object, *keys)
+
+ Return a copy of the object, filtered to only have values for
+ the whitelisted keys (or array of valid keys).
+
+_.pick({name : 'moe', age: 50, userid : 'moe1'}, 'name', 'age');
+=> {name : 'moe', age : 50}
+
+
+
+ defaults_.defaults(object, *defaults)
+
+ Fill in missing properties in object with default values from the
+ defaults objects, and return the object. As soon as the
+ property is filled, further defaults will have no effect.
+
+var iceCream = {flavor : "chocolate"};
+_.defaults(iceCream, {flavor : "vanilla", sprinkles : "lots"});
+=> {flavor : "chocolate", sprinkles : "lots"}
+
+
+
+ clone_.clone(object)
+
+ Create a shallow-copied clone of the object. Any nested objects
+ or arrays will be copied by reference, not duplicated.
+
+_.clone({name : 'moe'});
+=> {name : 'moe'};
+
+
+
+ tap_.tap(object, interceptor)
+
+ Invokes interceptor with the object, and then returns object.
+ The primary purpose of this method is to "tap into" a method chain, in order to perform operations on intermediate results within the chain.
+
+_.chain([1,2,3,200])
+ .filter(function(num) { return num % 2 == 0; })
+ .tap(alert)
+ .map(function(num) { return num * num })
+ .value();
+=> // [2, 200] (alerted)
+=> [4, 40000]
+
+
+
+ has_.has(object, key)
+
+ Does the object contain the given key? Identical to
+ object.hasOwnProperty(key), but uses a safe reference to the
+ hasOwnProperty function, in case it's been
+ overridden accidentally.
+
+_.has({a: 1, b: 2, c: 3}, "b");
+=> true
+
+
+
+ isEqual_.isEqual(object, other)
+
+ Performs an optimized deep comparison between the two objects, to determine
+ if they should be considered equal.
+
+var moe = {name : 'moe', luckyNumbers : [13, 27, 34]};
+var clone = {name : 'moe', luckyNumbers : [13, 27, 34]};
+moe == clone;
+=> false
+_.isEqual(moe, clone);
+=> true
+
+
+
+ isEmpty_.isEmpty(object)
+
+ Returns true if object contains no values.
+
+_.isEmpty([1, 2, 3]);
+=> false
+_.isEmpty({});
+=> true
+
+
+
+ isElement_.isElement(object)
+
+ Returns true if object is a DOM element.
+
+_.isElement(jQuery('body')[0]);
+=> true
+
+
+
+ isArray_.isArray(object)
+
+ Returns true if object is an Array.
+
+(function(){ return _.isArray(arguments); })();
+=> false
+_.isArray([1,2,3]);
+=> true
+
+
+
+ isObject_.isObject(value)
+
+ Returns true if value is an Object.
+
+_.isObject({});
+=> true
+_.isObject(1);
+=> false
+
+
+
+ isArguments_.isArguments(object)
+
+ Returns true if object is an Arguments object.
+
+(function(){ return _.isArguments(arguments); })(1, 2, 3);
+=> true
+_.isArguments([1,2,3]);
+=> false
+
+
+
+ isFunction_.isFunction(object)
+
+ Returns true if object is a Function.
+
+_.isFunction(alert); +=> true ++ +
+ isString_.isString(object)
+
+ Returns true if object is a String.
+
+_.isString("moe");
+=> true
+
+
+
+ isNumber_.isNumber(object)
+
+ Returns true if object is a Number (including NaN).
+
+_.isNumber(8.4 * 5); +=> true ++ +
+ isBoolean_.isBoolean(object)
+
+ Returns true if object is either true or false.
+
+_.isBoolean(null); +=> false ++ +
+ isDate_.isDate(object)
+
+ Returns true if object is a Date.
+
+_.isDate(new Date()); +=> true ++ +
+ isRegExp_.isRegExp(object)
+
+ Returns true if object is a RegExp.
+
+_.isRegExp(/moe/); +=> true ++ +
+ isNaN_.isNaN(object)
+
+ Returns true if object is NaN.
Note: this is not
+ the same as the native isNaN function, which will also return
+ true if the variable is undefined.
+
+_.isNaN(NaN); +=> true +isNaN(undefined); +=> true +_.isNaN(undefined); +=> false ++ +
+ isNull_.isNull(object)
+
+ Returns true if the value of object is null.
+
+_.isNull(null); +=> true +_.isNull(undefined); +=> false ++ +
+ isUndefined_.isUndefined(variable)
+
+ Returns true if variable is undefined.
+
+_.isUndefined(window.missingVariable); +=> true ++ +
+ noConflict_.noConflict()
+
+ Give control of the "_" variable back to its previous owner. Returns
+ a reference to the Underscore object.
+
+var underscore = _.noConflict();+ +
+ identity_.identity(value)
+
+ Returns the same value that is used as the argument. In math:
+ f(x) = x
+ This function looks useless, but is used throughout Underscore as
+ a default iterator.
+
+var moe = {name : 'moe'};
+moe === _.identity(moe);
+=> true
+
+
+ times_.times(n, iterator)
+
+ Invokes the given iterator function n times.
+
+_(3).times(function(){ genie.grantWish(); });
+
+
+ mixin_.mixin(object)
+
+ Allows you to extend Underscore with your own utility functions. Pass
+ a hash of {name: function} definitions to have your functions
+ added to the Underscore object, as well as the OOP wrapper.
+
+_.mixin({
+ capitalize : function(string) {
+ return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
+ }
+});
+_("fabio").capitalize();
+=> "Fabio"
+
+
+
+ uniqueId_.uniqueId([prefix])
+
+ Generate a globally-unique id for client-side models or DOM elements
+ that need one. If prefix is passed, the id will be appended to it.
+ Without prefix, returns an integer.
+
+_.uniqueId('contact_');
+=> 'contact_104'
+
+
+ escape_.escape(string)
+
+ Escapes a string for insertion into HTML, replacing
+ &, <, >, ", ', and / characters.
+
+_.escape('Curly, Larry & Moe');
+=> "Curly, Larry & Moe"
+
+
+ result_.result(object, property)
+
+ If the value of the named property is a function then invoke it; otherwise, return it.
+
+var object = {cheese: 'crumpets', stuff: function(){ return 'nonsense'; }};
+_.result(object, 'cheese');
+=> "crumpets"
+_.result(object, 'stuff');
+=> "nonsense"
+
+
+ template_.template(templateString, [data], [settings])
+
+ Compiles JavaScript templates into functions that can be evaluated
+ for rendering. Useful for rendering complicated bits of HTML from JSON
+ data sources. Template functions can both interpolate variables, using
+ <%= … %>, as well as execute arbitrary JavaScript code, with
+ <% … %>. If you wish to interpolate a value, and have
+ it be HTML-escaped, use <%- … %> When you evaluate a template function, pass in a
+ data object that has properties corresponding to the template's free
+ variables. If you're writing a one-off, you can pass the data
+ object as the second parameter to template in order to render
+ immediately instead of returning a template function. The settings argument
+ should be a hash containing any _.templateSettings that should be overriden.
+
+var compiled = _.template("hello: <%= name %>");
+compiled({name : 'moe'});
+=> "hello: moe"
+
+var list = "<% _.each(people, function(name) { %> <li><%= name %></li> <% }); %>";
+_.template(list, {people : ['moe', 'curly', 'larry']});
+=> "<li>moe</li><li>curly</li><li>larry</li>"
+
+var template = _.template("<b><%- value %></b>");
+template({value : '<script>'});
+=> "<b><script></b>"
+
+ + You can also use print from within JavaScript code. This is + sometimes more convenient than using <%= ... %>. +
+ +
+var compiled = _.template("<% print('Hello ' + epithet); %>");
+compiled({epithet: "stooge"});
+=> "Hello stooge."
+
+ + If ERB-style delimiters aren't your cup of tea, you can change Underscore's + template settings to use different symbols to set off interpolated code. + Define an interpolate regex to match expressions that should be + interpolated verbatim, an escape regex to match expressions that should + be inserted after being HTML escaped, and an evaluate regex to match + expressions that should be evaluated without insertion into the resulting + string. You may define or omit any combination of the three. + For example, to perform + Mustache.js + style templating: +
+ +
+_.templateSettings = {
+ interpolate : /\{\{(.+?)\}\}/g
+};
+
+var template = _.template("Hello {{ name }}!");
+template({name : "Mustache"});
+=> "Hello Mustache!"
+
+ + By default, template places the values from your data in the local scope + via the with statement. However, you can specify a single variable name + with the variable setting. +
+ +
+_.template("<%= data.hasWith %>", {hasWith: 'no'}, {variable: 'data'});
+=> "no"
+
+ + Precompiling your templates can be a big help when debugging errors you can't + reproduce. This is because precompiled templates can provide line numbers and + a stack trace, something that is not possible when compiling templates on the client. + template provides the source property on the compiled template + function for easy precompilation. +
+ +<script> + JST.project = <%= _.template(jstText).source %>; +</script>+ + +
+ You can use Underscore in either an object-oriented or a functional style, + depending on your preference. The following two lines of code are + identical ways to double a list of numbers. +
+ +
+_.map([1, 2, 3], function(n){ return n * 2; });
+_([1, 2, 3]).map(function(n){ return n * 2; });
+
+ + Calling chain on a wrapped object will cause all future method calls to + return wrapped objects as well. When you've finished the computation, + use value to retrieve the final value. Here's an example of chaining + together a map/flatten/reduce, in order to get the word count of + every word in a song. +
+ +
+var lyrics = [
+ {line : 1, words : "I'm a lumberjack and I'm okay"},
+ {line : 2, words : "I sleep all night and I work all day"},
+ {line : 3, words : "He's a lumberjack and he's okay"},
+ {line : 4, words : "He sleeps all night and he works all day"}
+];
+
+_.chain(lyrics)
+ .map(function(line) { return line.words.split(' '); })
+ .flatten()
+ .reduce(function(counts, word) {
+ counts[word] = (counts[word] || 0) + 1;
+ return counts;
+}, {}).value();
+
+=> {lumberjack : 2, all : 4, night : 2 ... }
+
+ + In addition, the + Array prototype's methods + are proxied through the chained Underscore object, so you can slip a + reverse or a push into your chain, and continue to + modify the array. +
+ +
+ chain_.chain(obj)
+
+ Returns a wrapped object. Calling methods on this object will continue
+ to return wrapped objects until value is used.
+
+var stooges = [{name : 'curly', age : 25}, {name : 'moe', age : 21}, {name : 'larry', age : 23}];
+var youngest = _.chain(stooges)
+ .sortBy(function(stooge){ return stooge.age; })
+ .map(function(stooge){ return stooge.name + ' is ' + stooge.age; })
+ .first()
+ .value();
+=> "moe is 21"
+
+
+
+ value_(obj).value()
+
+ Extracts the value of a wrapped object.
+
+_([1, 2, 3]).value(); +=> [1, 2, 3] ++ +
+ Underscore.lua, + a Lua port of the functions that are applicable in both languages. + Includes OOP-wrapping and chaining. + The source is + available on GitHub. +
+ ++ Underscore.php, + a PHP port of the functions that are applicable in both languages. + Includes OOP-wrapping and chaining. + The source is + available on GitHub. +
+ ++ Underscore-perl, + a Perl port of many of the Underscore.js functions, + aimed at on Perl hashes and arrays, also + available on GitHub. +
+ ++ Underscore.string, + an Underscore extension that adds functions for string-manipulation: + trim, startsWith, contains, capitalize, + reverse, sprintf, and more. +
+ ++ Ruby's Enumerable module. +
+ ++ Prototype.js, which provides + JavaScript with collection functions in the manner closest to Ruby's Enumerable. +
+ ++ Oliver Steele's + Functional JavaScript, + which includes comprehensive higher-order function support as well as string lambdas. +
+ ++ Michael Aufreiter's Data.js, + a data manipulation + persistence library for JavaScript. +
+ ++ Python's itertools. +
+ +
+ 1.3.1 — Jan. 23, 2012
+
+ 1.3.0 — Jan. 11, 2012
+
+ 1.2.4 — Jan. 4, 2012
+
+ 1.2.3 — Dec. 7, 2011
+
+ 1.2.2 — Nov. 14, 2011
+
+ 1.2.1 — Oct. 24, 2011
+
+ 1.2.0 — Oct. 5, 2011
+
+ 1.1.7 — July 13, 2011
+ Added _.groupBy, which aggregates a collection into groups of like items.
+ Added _.union and _.difference, to complement the
+ (re-named) _.intersection.
+ Various improvements for support of sparse arrays.
+ _.toArray now returns a clone, if directly passed an array.
+ _.functions now also returns the names of functions that are present
+ in the prototype chain.
+
+ 1.1.6 — April 18, 2011
+ Added _.after, which will return a function that only runs after
+ first being called a specified number of times.
+ _.invoke can now take a direct function reference.
+ _.every now requires an iterator function to be passed, which
+ mirrors the ECMA5 API.
+ _.extend no longer copies keys when the value is undefined.
+ _.bind now errors when trying to bind an undefined value.
+
+ 1.1.5 — Mar 20, 2011
+ Added an _.defaults function, for use merging together JS objects
+ representing default options.
+ Added an _.once function, for manufacturing functions that should
+ only ever execute a single time.
+ _.bind now delegates to the native ECMAScript 5 version,
+ where available.
+ _.keys now throws an error when used on non-Object values, as in
+ ECMAScript 5.
+ Fixed a bug with _.keys when used over sparse arrays.
+
+ 1.1.4 — Jan 9, 2011
+ Improved compliance with ES5's Array methods when passing null
+ as a value. _.wrap now correctly sets this for the
+ wrapped function. _.indexOf now takes an optional flag for
+ finding the insertion index in an array that is guaranteed to already
+ be sorted. Avoiding the use of .callee, to allow _.isArray
+ to work properly in ES5's strict mode.
+
+ 1.1.3 — Dec 1, 2010
+ In CommonJS, Underscore may now be required with just:
+ var _ = require("underscore").
+ Added _.throttle and _.debounce functions.
+ Removed _.breakLoop, in favor of an ECMA5-style un-break-able
+ each implementation — this removes the try/catch, and you'll now have
+ better stack traces for exceptions that are thrown within an Underscore iterator.
+ Improved the isType family of functions for better interoperability
+ with Internet Explorer host objects.
+ _.template now correctly escapes backslashes in templates.
+ Improved _.reduce compatibility with the ECMA5 version:
+ if you don't pass an initial value, the first item in the collection is used.
+ _.each no longer returns the iterated collection, for improved
+ consistency with ES5's forEach.
+
+ 1.1.2
+ Fixed _.contains, which was mistakenly pointing at
+ _.intersect instead of _.include, like it should
+ have been. Added _.unique as an alias for _.uniq.
+
+ 1.1.1
+ Improved the speed of _.template, and its handling of multiline
+ interpolations. Ryan Tenney contributed optimizations to many Underscore
+ functions. An annotated version of the source code is now available.
+
+ 1.1.0
+ The method signature of _.reduce has been changed to match
+ the ECMAScript 5 signature, instead of the Ruby/Prototype.js version.
+ This is a backwards-incompatible change. _.template may now be
+ called with no arguments, and preserves whitespace. _.contains
+ is a new alias for _.include.
+
+ 1.0.4
+ Andri Möll contributed the _.memoize
+ function, which can be used to speed up expensive repeated computations
+ by caching the results.
+
+ 1.0.3
+ Patch that makes _.isEqual return false if any property
+ of the compared object has a NaN value. Technically the correct
+ thing to do, but of questionable semantics. Watch out for NaN comparisons.
+
+ 1.0.2
+ Fixes _.isArguments in recent versions of Opera, which have
+ arguments objects as real Arrays.
+
+ 1.0.1
+ Bugfix for _.isEqual, when comparing two objects with the same
+ number of undefined keys, but with different names.
+
+ 1.0.0
+ Things have been stable for many months now, so Underscore is now
+ considered to be out of beta, at 1.0. Improvements since 0.6
+ include _.isBoolean, and the ability to have _.extend
+ take multiple source objects.
+
+ 0.6.0
+ Major release. Incorporates a number of
+ Mile Frawley's refactors for
+ safer duck-typing on collection functions, and cleaner internals. A new
+ _.mixin method that allows you to extend Underscore with utility
+ functions of your own. Added _.times, which works the same as in
+ Ruby or Prototype.js. Native support for ECMAScript 5's Array.isArray,
+ and Object.keys.
+
+ 0.5.8
+ Fixed Underscore's collection functions to work on
+ NodeLists and
+ HTMLCollections
+ once more, thanks to
+ Justin Tulloss.
+
+ 0.5.7
+ A safer implementation of _.isArguments, and a
+ faster _.isNumber,
thanks to
+ Jed Schmidt.
+
+ 0.5.6
+ Customizable delimiters for _.template, contributed by
+ Noah Sloan.
+
+ 0.5.5
+ Fix for a bug in MobileSafari's OOP-wrapper, with the arguments object.
+
+ 0.5.4
+ Fix for multiple single quotes within a template string for
+ _.template. See:
+ Rick Strahl's blog post.
+
+ 0.5.2
+ New implementations of isArray, isDate, isFunction,
+ isNumber, isRegExp, and isString, thanks to
+ a suggestion from
+ Robert Kieffer.
+ Instead of doing Object#toString
+ comparisons, they now check for expected properties, which is less safe,
+ but more than an order of magnitude faster. Most other Underscore
+ functions saw minor speed improvements as a result.
+ Evgeniy Dolzhenko
+ contributed _.tap,
+ similar to Ruby 1.9's,
+ which is handy for injecting side effects (like logging) into chained calls.
+
+ 0.5.1
+ Added an _.isArguments function. Lots of little safety checks
+ and optimizations contributed by
+ Noah Sloan and
+ Andri Möll.
+
+ 0.5.0
+ [API Changes] _.bindAll now takes the context object as
+ its first parameter. If no method names are passed, all of the context
+ object's methods are bound to it, enabling chaining and easier binding.
+ _.functions now takes a single argument and returns the names
+ of its Function properties. Calling _.functions(_) will get you
+ the previous behavior.
+ Added _.isRegExp so that isEqual can now test for RegExp equality.
+ All of the "is" functions have been shrunk down into a single definition.
+ Karl Guertin contributed patches.
+
+ 0.4.7
+ Added isDate, isNaN, and isNull, for completeness.
+ Optimizations for isEqual when checking equality between Arrays
+ or Dates. _.keys is now 25%–2X faster (depending on your
+ browser) which speeds up the functions that rely on it, such as _.each.
+
+ 0.4.6
+ Added the range function, a port of the
+ Python
+ function of the same name, for generating flexibly-numbered lists
+ of integers. Original patch contributed by
+ Kirill Ishanov.
+
+ 0.4.5
+ Added rest for Arrays and arguments objects, and aliased
+ first as head, and rest as tail,
+ thanks to Luke Sutton's patches.
+ Added tests ensuring that all Underscore Array functions also work on
+ arguments objects.
+
+ 0.4.4
+ Added isString, and isNumber, for consistency. Fixed
+ _.isEqual(NaN, NaN) to return true (which is debatable).
+
+ 0.4.3
+ Started using the native StopIteration object in browsers that support it.
+ Fixed Underscore setup for CommonJS environments.
+
+ 0.4.2
+ Renamed the unwrapping function to value, for clarity.
+
+ 0.4.1
+ Chained Underscore objects now support the Array prototype methods, so
+ that you can perform the full range of operations on a wrapped array
+ without having to break your chain. Added a breakLoop method
+ to break in the middle of any Underscore iteration. Added an
+ isEmpty function that works on arrays and objects.
+
+ 0.4.0
+ All Underscore functions can now be called in an object-oriented style,
+ like so: _([1, 2, 3]).map(...);. Original patch provided by
+ Marc-André Cournoyer.
+ Wrapped objects can be chained through multiple
+ method invocations. A functions method
+ was added, providing a sorted list of all the functions in Underscore.
+
+ 0.3.3
+ Added the JavaScript 1.8 function reduceRight. Aliased it
+ as foldr, and aliased reduce as foldl.
+
+ 0.3.2
+ Now runs on stock Rhino
+ interpreters with: load("underscore.js").
+ Added identity as a utility function.
+
+ 0.3.1
+ All iterators are now passed in the original collection as their third
+ argument, the same as JavaScript 1.6's forEach. Iterating over
+ objects is now called with (value, key, collection), for details
+ see _.each.
+
+ 0.3.0
+ Added Dmitry Baranovskiy's
+ comprehensive optimizations, merged in
+ Kris Kowal's patches to make Underscore
+ CommonJS and
+ Narwhal compliant.
+
+ 0.2.0
+ Added compose and lastIndexOf, renamed inject to
+ reduce, added aliases for inject, filter,
+ every, some, and forEach.
+
+ 0.1.1
+ Added noConflict, so that the "Underscore" object can be assigned to
+ other variables.
+
+ 0.1.0
+ Initial release of Underscore.js.
+
+
+
+
+
+ A representative sample of the functions are benchmarked here, to provide
+ a sense of how fast they might run in different browsers.
+ Each iteration runs on an array of 1000 elements.
+ For example, the 'intersect' test measures the number of times you can
+ find the intersection of two thousand-element arrays in one second.
+
Just some text. Hey, I know this is silly but it aids consistency.
Just some text. Hey, I know this is silly but it aids consistency.