diff --git a/README b/README index 333b13f45..c7eb97bd1 100644 --- a/README +++ b/README @@ -27,5 +27,6 @@ Many thanks to our contributors: Kris Kowal Jed Schmidt Noah Sloan + Nick Stenning Luke Sutton John Wright diff --git a/index.html b/index.html index 289bbb520..597d8f602 100644 --- a/index.html +++ b/index.html @@ -110,12 +110,12 @@

Downloads (Right-click, and u - - + + - - + +
Development Version (1.0.4)24kb, Uncompressed with CommentsDevelopment Version (1.1.0)25kb, Uncompressed with Comments
Production Version (1.0.4)2.9kb, Packed and GzippedProduction Version (1.1.0)3kb, Packed and Gzipped
@@ -216,10 +216,10 @@

Object-Oriented and Functional Styles

_(lyrics).chain() .map(function(line) { return line.words.split(' '); }) .flatten() - .reduce({}, function(counts, word) { + .reduce(function(counts, word) { counts[word] = (counts[word] || 0) + 1; return counts; -}).value(); +}, {}).value(); => {lumberjack : 2, all : 4, night : 2 ... } @@ -261,7 +261,7 @@

Collection Functions (Arrays or Objects)

=> [3, 6, 9]

- reduce_.reduce(list, memo, iterator, [context]) + reduce_.reduce(list, iterator, memo, [context]) Aliases: inject, foldl
Also known as inject and foldl, reduce boils down a @@ -270,12 +270,12 @@

Collection Functions (Arrays or Objects)

iterator.

-var sum = _.reduce([1, 2, 3], 0, function(memo, num){ return memo + num });
+var sum = _.reduce([1, 2, 3], function(memo, num){ return memo + num }, 0);
 => 6
 

- reduceRight_.reduceRight(list, memo, iterator, [context]) + reduceRight_.reduceRight(list, iterator, memo, [context]) Alias: foldr
The right-associative version of reduce. Delegates to the @@ -285,7 +285,7 @@

Collection Functions (Arrays or Objects)

 var list = [[0, 1], [2, 3], [4, 5]];
-var flat = _.reduceRight(list, [], function(a, b) { return a.concat(b); });
+var flat = _.reduceRight(list, function(a, b) { return a.concat(b); }, []);
 => [4, 5, 2, 3, 0, 1]
 
@@ -356,6 +356,7 @@

Collection Functions (Arrays or Objects)

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 @@ -1128,6 +1129,13 @@

Links & Suggested Reading

The source is available on GitHub.

+ +

+ Underscore.strings, + an Underscore extension that adds functions for string-manipulation: + trim, startsWith, contains, capitalize, + reverse, sprintf, and more. +

Ruby's Enumerable module. @@ -1150,10 +1158,20 @@

Links & Suggested Reading

Change Log

+

+ 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. + Andri Möll contributed the _.memoize + function, which can be used to speed up expensive repeated computations + by caching the results.

@@ -1248,7 +1266,8 @@

Change Log

0.5.1
Added an _.isArguments function. Lots of little safety checks and optimizations contributed by - Noah Sloan and Andri Möll. + Noah Sloan and + Andri Möll.

diff --git a/package.json b/package.json index 5a1d90d4a..f00dacc03 100644 --- a/package.json +++ b/package.json @@ -8,5 +8,5 @@ "dependencies" : [], "lib" : ".", "main" : "underscore.js", - "version" : "1.0.4" + "version" : "1.1.0" } diff --git a/test/chaining.js b/test/chaining.js index 55f0c8c0d..e633ba5ad 100644 --- a/test/chaining.js +++ b/test/chaining.js @@ -12,11 +12,11 @@ $(document).ready(function() { var counts = _(lyrics).chain() .map(function(line) { return line.split(''); }) .flatten() - .reduce({}, function(hash, l) { + .reduce(function(hash, l) { hash[l] = hash[l] || 0; hash[l]++; return hash; - }).value(); + }, {}).value(); ok(counts['a'] == 16 && counts['e'] == 10, 'counted all the letters in the song'); }); diff --git a/test/collections.js b/test/collections.js index 3759a6495..9c72e889e 100644 --- a/test/collections.js +++ b/test/collections.js @@ -53,22 +53,22 @@ $(document).ready(function() { }); test('collections: reduce', function() { - var sum = _.reduce([1, 2, 3], 0, function(sum, num){ return sum + num; }); + var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; }, 0); equals(sum, 6, 'can sum up an array'); var context = {multiplier : 3}; - sum = _.reduce([1, 2, 3], 0, function(sum, num){ return sum + num * this.multiplier; }, context); + sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num * this.multiplier; }, 0, context); equals(sum, 18, 'can reduce with a context object'); - sum = _.inject([1, 2, 3], 0, function(sum, num){ return sum + num; }); + sum = _.inject([1, 2, 3], function(sum, num){ return sum + num; }, 0); equals(sum, 6, 'aliased as "inject"'); - sum = _([1, 2, 3]).reduce(0, function(sum, num){ return sum + num; }); + sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0); equals(sum, 6, 'OO-style reduce'); }); test('collections: reduceRight', function() { - var list = _.foldr([1, 2, 3], '', function(memo, num){ return memo + num; }); + var list = _.foldr([1, 2, 3], function(memo, num){ return memo + num; }, ''); equals(list, '321', 'can perform right folds'); }); @@ -111,7 +111,7 @@ $(document).ready(function() { test('collections: include', function() { ok(_.include([1,2,3], 2), 'two is in the array'); ok(!_.include([1,3,9], 2), 'two is not in the array'); - ok(_.include({moe:1, larry:3, curly:9}, 3), '_.include on objects checks their values'); + ok(_.contains({moe:1, larry:3, curly:9}, 3), '_.include on objects checks their values'); ok(_([1,2,3]).include(2), 'OO-style include'); }); diff --git a/test/objects.js b/test/objects.js index 7dadb8476..fc9c93d42 100644 --- a/test/objects.js +++ b/test/objects.js @@ -12,7 +12,7 @@ $(document).ready(function() { test("objects: functions", function() { var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact", - "compose","defer", "delay", "detect", "each", "every", "extend", "filter", "first", + "compose", "contains", "defer", "delay", "detect", "each", "every", "extend", "filter", "first", "flatten", "foldl", "foldr", "forEach", "functions", "head", "identity", "include", "indexOf", "inject", "intersect", "invoke", "isArguments", "isArray", "isBoolean", "isDate", "isElement", "isEmpty", "isEqual", "isFunction", "isNaN", "isNull", "isNumber", "isRegExp", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max", diff --git a/test/utility.js b/test/utility.js index c9498c227..8670a9fda 100644 --- a/test/utility.js +++ b/test/utility.js @@ -59,12 +59,19 @@ $(document).ready(function() { result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equals(result, "

", 'can run arbitrary javascript in templates'); + var noInterpolateTemplate = _.template("

Just some text. Hey, I know this is silly but it aids consistency.

"); + result = noInterpolateTemplate(); + equals(result, "

Just some text. Hey, I know this is silly but it aids consistency.

"); + var quoteTemplate = _.template("It's its, not it's"); equals(quoteTemplate({}), "It's its, not it's"); var quoteInStatementAndBody = _.template("<% if(foo == 'bar'){ %>Statement quotes and 'quotes'.<% } %>"); equals(quoteInStatementAndBody({foo: "bar"}), "Statement quotes and 'quotes'."); + var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.'); + equals(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.'); + _.templateSettings = { start : '{{', end : '}}', diff --git a/underscore-min.js b/underscore-min.js index 2ee918c5d..18fed07f9 100644 --- a/underscore-min.js +++ b/underscore-min.js @@ -1,10 +1,10 @@ -(function(){var n=this,A=n._,r=typeof StopIteration!=="undefined"?StopIteration:"__break__",j=Array.prototype,l=Object.prototype,o=j.slice,B=j.unshift,C=l.toString,p=l.hasOwnProperty,s=j.forEach,t=j.map,u=j.reduce,v=j.reduceRight,w=j.filter,x=j.every,y=j.some,m=j.indexOf,z=j.lastIndexOf;l=Array.isArray;var D=Object.keys,b=function(a){return new k(a)};if(typeof exports!=="undefined")exports._=b;n._=b;b.VERSION="1.0.4";var i=b.forEach=function(a,c,d){try{if(s&&a.forEach===s)a.forEach(c,d);else if(b.isNumber(a.length))for(var e= -0,f=a.length;e=e.computed&&(e={value:f,computed:g})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};i(a,function(f,g,h){g=c?c.call(d,f,g,h):f;gh? 1:0}),"value")};b.sortedIndex=function(a,c,d){d=d||b.identity;for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e0?f-c:c-f)>=0)return e;e[g++]=f}};b.bind=function(a,c){var d=b.rest(arguments,2);return function(){return a.apply(c||{},d.concat(b.toArray(arguments)))}};b.bindAll=function(a){var c=b.rest(arguments);if(c.length==0)c=b.functions(a);i(c,function(d){a[d]=b.bind(a[d],a)});return a};b.memoize=function(a,c){var d={};c=c||b.identity;return function(){var e=c.apply(this,arguments);return e in d?d[e]:(d[e]=a.apply(this,arguments))}};b.delay=function(a,c){var d=b.rest(arguments,2);return setTimeout(function(){return a.apply(a,d)},c)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(b.rest(arguments)))};b.wrap=function(a,c){return function(){var d=[a].concat(b.toArray(arguments));return c.apply(c,d)}};b.compose=function(){var a=b.toArray(arguments);return function(){for(var c=b.toArray(arguments),d=a.length-1;d>=0;d--)c=[a[d].apply(this,c)];return c[0]}};b.keys=D||function(a){if(b.isArray(a))return b.range(0, @@ -12,6 +12,7 @@ a.length);var c=[];for(var d in a)p.call(a,d)&&c.push(d);return c};b.values=func if(a==c)return true;if(!a&&c||a&&!c)return false;if(a.isEqual)return a.isEqual(c);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return false;if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return false;if(a.length&&a.length!==c.length)return false;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!(f in c)||!b.isEqual(a[f], c[f]))return false;return true};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(p.call(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=l||function(a){return!!(a&&a.concat&&a.unshift&&!a.callee)};b.isArguments=function(a){return a&&a.callee};b.isFunction=function(a){return!!(a&&a.constructor&&a.call&&a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return a=== +a||C.call(a)==="[object Number]"};b.isBoolean=function(a){return a===true||a===false};b.isDate=function(a){return!!(a&&a.getTimezoneOffset&&a.setUTCFullYear)};b.isRegExp=function(a){return!!(a&&a.test&&a.exec&&(a.ignoreCase||a.ignoreCase===false))};b.isNaN=function(a){return b.isNumber(a)&&isNaN(a)};b.isNull=function(a){return a===null};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){n._=A;return this};b.identity=function(a){return a};b.times=function(a,c,d){for(var e= -0;e",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+d.end.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")+")","g");d=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g, -" ").replace(e,"\t").split("'").join("\\'").split("\t").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?d(c):d};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain():a},E=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments); -B.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain=true;return this};k.prototype.value=function(){return this._wrapped}})(); +0;e",interpolate:/<%=(.+?)%>/g};b.template=function(a,c){var d=b.templateSettings,e=new RegExp("'(?=[^"+d.end.substr(0,1)+"]*"+d.end.replace(/([.*+?^${}()|[\]\/\\])/g,"\\$1")+")","g");d=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj||{}){p.push('"+a.replace(/\r/g, +"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t").replace(e,"\u2704").split("'").join("\\'").split("\u2704").join("'").replace(d.interpolate,"',$1,'").split(d.start).join("');").split(d.end).join("p.push('")+"');}return p.join('');");return c?d(c):d};b.each=b.forEach;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.select=b.filter;b.all=b.every;b.any=b.some;b.contains=b.include;b.head=b.first;b.tail=b.rest;b.methods=b.functions;var k=function(a){this._wrapped=a},q=function(a,c){return c?b(a).chain(): +a},E=function(a,c){k.prototype[a]=function(){var d=b.toArray(arguments);B.call(d,this._wrapped);return q(c.apply(b,d),this._chain)}};b.mixin(b);i(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var c=j[a];k.prototype[a]=function(){c.apply(this._wrapped,arguments);return q(this._wrapped,this._chain)}});i(["concat","join","slice"],function(a){var c=j[a];k.prototype[a]=function(){return q(c.apply(this._wrapped,arguments),this._chain)}});k.prototype.chain=function(){this._chain= +true;return this};k.prototype.value=function(){return this._wrapped}})(); diff --git a/underscore.js b/underscore.js index 1170caf8d..60bf47ccd 100644 --- a/underscore.js +++ b/underscore.js @@ -55,7 +55,7 @@ root._ = _; // Current version. - _.VERSION = '1.0.4'; + _.VERSION = '1.1.0'; // ------------------------ Collection Functions: --------------------------- @@ -92,8 +92,11 @@ // Reduce builds up a single result from a list of values, aka inject, or foldl. // Delegates to JavaScript 1.8's native reduce if available. - _.reduce = function(obj, memo, iterator, context) { - if (nativeReduce && obj.reduce === nativeReduce) return obj.reduce(_.bind(iterator, context), memo); + _.reduce = function(obj, iterator, memo, context) { + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return obj.reduce(iterator, memo); + } each(obj, function(value, index, list) { memo = iterator.call(context, memo, value, index, list); }); @@ -102,10 +105,13 @@ // The right-associative version of reduce, also known as foldr. Uses // Delegates to JavaScript 1.8's native reduceRight if available. - _.reduceRight = function(obj, memo, iterator, context) { - if (nativeReduceRight && obj.reduceRight === nativeReduceRight) return obj.reduceRight(_.bind(iterator, context), memo); + _.reduceRight = function(obj, iterator, memo, context) { + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return obj.reduceRight(iterator, memo); + } var reversed = _.clone(_.toArray(obj)).reverse(); - return _.reduce(reversed, memo, iterator, context); + return _.reduce(reversed, iterator, memo, context); }; // Return the first value which passes a truth test. @@ -277,11 +283,11 @@ // Return a completely flattened version of an array. _.flatten = function(array) { - return _.reduce(array, [], function(memo, value) { + return _.reduce(array, function(memo, value) { if (_.isArray(value)) return memo.concat(_.flatten(value)); memo.push(value); return memo; - }); + }, []); }; // Return a version of the array that does not contain the specified value(s). @@ -293,10 +299,10 @@ // Produce a duplicate-free version of the array. If the array has already // been sorted, you have the option of using a faster algorithm. _.uniq = function(array, isSorted) { - return _.reduce(array, [], function(memo, el, i) { + return _.reduce(array, function(memo, el, i) { if (0 == i || (isSorted === true ? _.last(memo) != el : !_.include(memo, el))) memo.push(el); return memo; - }); + }, []); }; // Produce an array that contains every item shared between all the @@ -619,17 +625,19 @@ // JavaScript templating a-la ERB, pilfered from John Resig's // "Secrets of the JavaScript Ninja", page 83. // Single-quote fix from Rick Strahl's version. - // With alterations for arbitrary delimiters. + // With alterations for arbitrary delimiters, and to preserve whitespace. _.template = function(str, data) { var c = _.templateSettings; var endMatch = new RegExp("'(?=[^"+c.end.substr(0, 1)+"]*"+escapeRegExp(c.end)+")","g"); var fn = new Function('obj', 'var p=[],print=function(){p.push.apply(p,arguments);};' + - 'with(obj){p.push(\'' + - str.replace(/[\r\t\n]/g, " ") - .replace(endMatch,"\t") + 'with(obj||{}){p.push(\'' + + str.replace(/\r/g, '\\r') + .replace(/\n/g, '\\n') + .replace(/\t/g, '\\t') + .replace(endMatch,"✄") .split("'").join("\\'") - .split("\t").join("'") + .split("✄").join("'") .replace(c.interpolate, "',$1,'") .split(c.start).join("');") .split(c.end).join("p.push('") @@ -645,6 +653,7 @@ _.select = _.filter; _.all = _.every; _.any = _.some; + _.contains = _.include; _.head = _.first; _.tail = _.rest; _.methods = _.functions;