diff --git a/index.html b/index.html index ff1a6dc9e..c2b1ac241 100644 --- a/index.html +++ b/index.html @@ -107,11 +107,11 @@

- + - +
Development Version (0.4.4)Development Version (0.4.5) 18kb, Uncompressed with Comments
Production Version (0.4.4)Production Version (0.4.5) 2kb, Packed and Gzipped
@@ -183,7 +183,7 @@ _(lyrics).chain()

Arrays
- first, last, + first, rest, last, compact, flatten, without, uniq, intersect, zip, indexOf, lastIndexOf @@ -454,15 +454,33 @@ _.size({one : 1, two : 2, three : 3});

Array Functions

+ +

+ Note: All array functions will also work on the arguments object. +

- first_.first(array) + first_.first(array, [n]) + Alias: head
- Convenience to return the first element of an array (identical to array[0]). + Returns the first element of an array. Passing n will + return the first n elements of the array.

-_.first([3, 2, 1]);
-=> 3
+_.first([5, 4, 3, 2, 1]);
+=> 5
+
+ +

+ 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]
 

@@ -471,7 +489,7 @@ _.first([3, 2, 1]); Returns the last element of an array.

-_.last([3, 2, 1]);
+_.last([5, 4, 3, 2, 1]);
 => 1
 
@@ -912,6 +930,15 @@ _([1, 2, 3]).value();

Change Log

+

+ 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 diff --git a/test/arrays.js b/test/arrays.js index a9dcb4e90..5464756f6 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -1,76 +1,89 @@ $(document).ready(function() { - + module("Array-only functions (last, compact, uniq, and so on...)"); - + test("arrays: first", function() { equals(_.first([1,2,3]), 1, 'can pull out the first element of an array'); equals(_([1, 2, 3]).first(), 1, 'can perform OO-style "first()"'); + equals(_.first([1,2,3], 2).join(', '), '1, 2', 'can pass an index to first'); + var result = (function(){ return _.first(arguments); })(4, 3, 2, 1); + equals(result, 4, 'works on an arguments object.'); }); - + + test("arrays: rest", function() { + var numbers = [1, 2, 3, 4]; + equals(_.rest(numbers).join(", "), "2, 3, 4", 'working rest()'); + equals(_.rest(numbers, 2).join(', '), '3, 4', 'rest can take an index'); + var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4); + equals(result.join(', '), '2, 3, 4', 'aliased as tail and works on arguments object'); + }); + test("arrays: last", function() { equals(_.last([1,2,3]), 3, 'can pull out the last element of an array'); + var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4); + equals(result, 4, 'works on an arguments object'); }); - + test("arrays: compact", function() { equals(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values'); + var result = (function(){ return _(arguments).compact().length; })(0, 1, false, 2, false, 3); + equals(result, 3, 'works on an arguments object'); }); - + test("arrays: flatten", function() { var list = [1, [2], [3, [[[4]]]]]; equals(_.flatten(list).join(', '), '1, 2, 3, 4', 'can flatten nested arrays'); + var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]); + equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); }); - + test("arrays: without", function() { var list = [1, 2, 1, 0, 3, 1, 4]; equals(_.without(list, 0, 1).join(', '), '2, 3, 4', 'can remove all instances of an object'); + var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4); + equals(result.join(', '), '2, 3, 4', 'works on an arguments object'); }); - + test("arrays: uniq", function() { var list = [1, 2, 1, 3, 1, 4]; equals(_.uniq(list).join(', '), '1, 2, 3, 4', 'can find the unique values of an unsorted array'); - + var list = [1, 1, 1, 2, 2, 3]; equals(_.uniq(list, true).join(', '), '1, 2, 3', 'can find the unique values of a sorted array faster'); + + var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4); + equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object'); }); - + test("arrays: intersect", function() { var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho']; equals(_.intersect(stooges, leaders).join(''), 'moe', 'can take the set intersection of two arrays'); equals(_(stooges).intersect(leaders).join(''), 'moe', 'can perform an OO-style intersection'); + var result = (function(){ return _.intersect(arguments, leaders); })('moe', 'curly', 'larry'); + equals(result.join(''), 'moe', 'works an an arguments object'); }); - + test('arrays: zip', function() { var names = ['moe', 'larry', 'curly'], ages = [30, 40, 50], leaders = [true]; var stooges = _.zip(names, ages, leaders); equals(String(stooges), 'moe,30,true,larry,40,,curly,50,', 'zipped together arrays of different lengths'); }); - + test("arrays: indexOf", function() { var numbers = [1, 2, 3]; numbers.indexOf = null; equals(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function'); + var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3); + equals(result, 1, 'works on an arguments object'); }); - + test("arrays: lastIndexOf", function() { var numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0]; numbers.lastIndexOf = null; equals(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function'); equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); + var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0); + equals(result, 5, 'works on an arguments object'); }); - - test("arrays: tail", function() { - var numbers = [1, 2, 3, 4]; - equals(_.tail(numbers).join(", "), "2, 3, 4"); - }); - - test("arrays: init", function() { - var numbers = [1, 2, 3, 4]; - equals(_.init(numbers).join(", "), "1, 2, 3"); - }); - - test("arrays: reverse", function() { - var numbers = [1, 2, 4, 6]; - equals(_.reverse(numbers).join(", "), "6, 4, 2, 1"); - }); - + }); diff --git a/test/utility.js b/test/utility.js index 897d0ceec..6fd19af93 100644 --- a/test/utility.js +++ b/test/utility.js @@ -31,12 +31,12 @@ $(document).ready(function() { }); test("utility: functions", function() { - var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact", + var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact", "compose","defer", "delay", "detect", "each", "every", "extend", "filter", "first", "flatten", "foldl", "foldr", "forEach", "functions", "head", "identity", "include", - "indexOf", "init", "inject", "intersect", "invoke", "isArray", "isElement", "isEmpty", "isEqual", + "indexOf", "inject", "intersect", "invoke", "isArray", "isElement", "isEmpty", "isEqual", "isFunction", "isNumber", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max", - "methods", "min", "pluck", "reduce", "reduceRight", "reject", "reverse", "select", + "methods", "min", "pluck", "reduce", "reduceRight", "reject", "rest", "select", "size", "some", "sortBy", "sortedIndex", "tail", "template", "toArray", "uniq", "uniqueId", "values", "without", "wrap", "zip"]; ok(_(expected).isEqual(_.methods()), 'provides a sorted list of functions'); diff --git a/underscore-min.js b/underscore-min.js index dbaff85cd..f8466a228 100644 --- a/underscore-min.js +++ b/underscore-min.js @@ -1,14 +1,14 @@ -(function(){var j=this,m=j._;function i(a){this._wrapped=a}var l=typeof StopIteration!=="undefined"?StopIteration:"__break__",b=j._=function(a){return new i(a)};if(typeof exports!=="undefined")exports._=b;b.VERSION="0.4.4";b.each=function(a,c,d){try{if(a.forEach)a.forEach(c,d);else if(a.length)for(var e=0,f=a.length;e=e.computed&&(e={value:f,computed:g})}); +return e};b.include=function(a,c){if(b.isArray(a))return b.indexOf(a,c)!=-1;var d=false;b.each(a,function(e){if(d=e===c)b.breakLoop()});return d};b.invoke=function(a,c){var d=b.rest(arguments,2);return b.map(a,function(e){return(c?e[c]:e).apply(e,d)})};b.pluck=function(a,c){return b.map(a,function(d){return d[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a))return Math.max.apply(Math,a);var e={computed:-Infinity};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;g>=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};b.each(a,function(f,g,h){g=c?c.call(d,f,g,h):f;gf?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;e=0;c--)arguments=[a[c].apply(this,arguments)];return arguments[0]}};b.keys=function(a){return b.map(a,function(c,d){return d})};b.values=function(a){return b.map(a,b.identity)};b.extend=function(a,c){for(var d in c)a[d]= -c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a,e=typeof c;if(d!=e)return false;if(a==c)return true;if(a.isEqual)return a.isEqual(c);if(b.isNumber(a)&&b.isNumber(c)&&isNaN(a)&&isNaN(c))return true;if(d!=="object")return false;d=b.keys(a);e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true};b.isEmpty=function(a){return(b.isArray(a)?a:b.values(a)).length== -0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return Object.prototype.toString.call(a)=="[object Array]"};b.isFunction=function(a){return Object.prototype.toString.call(a)=="[object Function]"};b.isString=function(a){return Object.prototype.toString.call(a)=="[object String]"};b.isNumber=function(a){return Object.prototype.toString.call(a)=="[object Number]"};b.isUndefined=function(a){return typeof a=="undefined"};b.noConflict=function(){j._=m;return this};b.identity= -function(a){return a};b.breakLoop=function(){throw l;};var n=0;b.uniqueId=function(a){var c=n++;return a?a+c:c};b.functions=function(){var a=[];for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&a.push(c);return b.without(a,"VERSION","prototype","noConflict").sort()};b.template=function(a,c){a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g,"$1\r").replace(/\t=(.*?)%>/g, -"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.methods=b.functions;function k(a,c){return c?b(a).chain():a}b.each(b.functions(),function(a){i.prototype[a]=function(){Array.prototype.unshift.call(arguments,this._wrapped);return k(b[a].apply(b,arguments),this._chain)}});b.each(["pop","push","reverse","shift", -"sort","splice","unshift"],function(a){i.prototype[a]=function(){Array.prototype[a].apply(this._wrapped,arguments);return k(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){i.prototype[a]=function(){return k(Array.prototype[a].apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})(); +(e=g+1):(f=g)}return e};b.toArray=function(a){if(!a)return[];if(b.isArray(a))return a;return b.map(a,function(c){return c})};b.size=function(a){return b.toArray(a).length};b.first=function(a,c){return c?Array.prototype.slice.call(a,0,c):a[0]};b.rest=function(a,c){return Array.prototype.slice.call(a,b.isUndefined(c)?1:c)};b.last=function(a){return a[a.length-1]};b.compact=function(a){return b.select(a,function(c){return!!c})};b.flatten=function(a){return b.reduce(a,[],function(c,d){if(b.isArray(d))return c.concat(b.flatten(d)); +c.push(d);return c})};b.without=function(a){var c=b.rest(arguments);return b.select(a,function(d){return!b.include(c,d)})};b.uniq=function(a,c){return b.reduce(a,[],function(d,e,f){if(0==f||(c?b.last(d)!=e:!b.include(d,e)))d.push(e);return d})};b.intersect=function(a){var c=b.rest(arguments);return b.select(b.uniq(a),function(d){return b.all(c,function(e){return b.indexOf(e,d)>=0})})};b.zip=function(){for(var a=b.toArray(arguments),c=b.max(b.pluck(a,"length")),d=new Array(c),e=0;e=0;c--)arguments=[a[c].apply(this,arguments)];return arguments[0]}};b.keys=function(a){return b.map(a,function(c,d){return d})};b.values=function(a){return b.map(a, +b.identity)};b.extend=function(a,c){for(var d in c)a[d]=c[d];return a};b.clone=function(a){if(b.isArray(a))return a.slice(0);return b.extend({},a)};b.isEqual=function(a,c){if(a===c)return true;var d=typeof a,e=typeof c;if(d!=e)return false;if(a==c)return true;if(a.isEqual)return a.isEqual(c);if(b.isNumber(a)&&b.isNumber(c)&&isNaN(a)&&isNaN(c))return true;if(d!=="object")return false;d=b.keys(a);e=b.keys(c);if(d.length!=e.length)return false;for(var f in a)if(!b.isEqual(a[f],c[f]))return false;return true}; +b.isEmpty=function(a){return(b.isArray(a)?a:b.values(a)).length==0};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=function(a){return Object.prototype.toString.call(a)=="[object Array]"};b.isFunction=function(a){return Object.prototype.toString.call(a)=="[object Function]"};b.isString=function(a){return Object.prototype.toString.call(a)=="[object String]"};b.isNumber=function(a){return Object.prototype.toString.call(a)=="[object Number]"};b.isUndefined=function(a){return typeof a== +"undefined"};b.noConflict=function(){j._=m;return this};b.identity=function(a){return a};b.breakLoop=function(){throw l;};var n=0;b.uniqueId=function(a){var c=n++;return a?a+c:c};b.functions=function(){var a=[];for(var c in b)Object.prototype.hasOwnProperty.call(b,c)&&a.push(c);return b.without(a,"VERSION","prototype","noConflict").sort()};b.template=function(a,c){a=new Function("obj","var p=[],print=function(){p.push.apply(p,arguments);};with(obj){p.push('"+a.replace(/[\r\t\n]/g," ").split("<%").join("\t").replace(/((^|%>)[^\t]*)'/g, +"$1\r").replace(/\t=(.*?)%>/g,"',$1,'").split("\t").join("');").split("%>").join("p.push('").split("\r").join("\\'")+"');}return p.join('');");return c?a(c):a};b.forEach=b.each;b.foldl=b.inject=b.reduce;b.foldr=b.reduceRight;b.filter=b.select;b.every=b.all;b.some=b.any;b.head=b.first;b.tail=b.rest;b.methods=b.functions;function k(a,c){return c?b(a).chain():a}b.each(b.functions(),function(a){i.prototype[a]=function(){Array.prototype.unshift.call(arguments,this._wrapped);return k(b[a].apply(b,arguments), +this._chain)}});b.each(["pop","push","reverse","shift","sort","splice","unshift"],function(a){i.prototype[a]=function(){Array.prototype[a].apply(this._wrapped,arguments);return k(this._wrapped,this._chain)}});b.each(["concat","join","slice"],function(a){i.prototype[a]=function(){return k(Array.prototype[a].apply(this._wrapped,arguments),this._chain)}});i.prototype.chain=function(){this._chain=true;return this};i.prototype.value=function(){return this._wrapped}})(); diff --git a/underscore.js b/underscore.js index ad6dbecac..69851fb02 100644 --- a/underscore.js +++ b/underscore.js @@ -31,7 +31,7 @@ if (typeof exports !== 'undefined') exports._ = _; // Current version. - _.VERSION = '0.4.4'; + _.VERSION = '0.4.5'; /*------------------------ Collection Functions: ---------------------------*/ @@ -156,7 +156,7 @@ // Invoke a method with arguments on every item in a collection. _.invoke = function(obj, method) { - var args = _.toArray(arguments).slice(2); + var args = _.rest(arguments, 2); return _.map(obj, function(value) { return (method ? value[method] : value).apply(value, args); }); @@ -228,9 +228,17 @@ /*-------------------------- Array Functions: ------------------------------*/ - // Get the first element of an array. - _.first = function(array) { - return array[0]; + // Get the first element of an array. Passing "n" will return the first N + // values in the array. Aliased as "head". + _.first = function(array, n) { + return n ? Array.prototype.slice.call(array, 0, n) : array[0]; + }; + + // Returns everything but the first entry of the array. Aliased as "tail". + // Especially useful on the arguments object. Passing an "index" will return + // the rest of the values in the array from that index onward. + _.rest = function(array, index) { + return Array.prototype.slice.call(array, _.isUndefined(index) ? 1 : index); }; // Get the last element of an array. @@ -254,7 +262,7 @@ // Return a version of the array that does not contain the specified value(s). _.without = function(array) { - var values = array.slice.call(arguments, 0); + var values = _.rest(arguments); return _.select(array, function(value){ return !_.include(values, value); }); }; @@ -270,7 +278,7 @@ // Produce an array that contains every item shared between all the // passed-in arrays. _.intersect = function(array) { - var rest = _.toArray(arguments).slice(1); + var rest = _.rest(arguments); return _.select(_.uniq(array), function(item) { return _.all(rest, function(other) { return _.indexOf(other, item) >= 0; @@ -305,49 +313,23 @@ while (i--) if (array[i] === item) return i; return -1; }; - - // Returns everything but the first entry of the array. Conceptually the - // same as calling shift(), but doesn't mutate the array passed in. - _.tail = function(array) { - var tail = _.clone(array); - tail.shift(); - return tail; - }; - - // Returns everything but the last entry of the array. Conceptually the - // same as calling pop(), but doesn't mutate the array passed in. - _.init = function(array) { - var init = _.clone(array); - init.pop(); - return init; - }; - - // Returns a new array, with the entries or the passed-in array in reverse - // order. - _.reverse = function(array) { - var reverse = _.clone(array); - return reverse.reverse(); - }; /* ----------------------- Function Functions: -----------------------------*/ // Create a function bound to a given object (assigning 'this', and arguments, // optionally). Binding with arguments is also known as 'curry'. _.bind = function(func, context) { - context = context || root; - var args = _.toArray(arguments).slice(2); + var args = _.rest(arguments, 2); return function() { - var a = args.concat(_.toArray(arguments)); - return func.apply(context, a); + return func.apply(context || root, args.concat(_.toArray(arguments))); }; }; // 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() { - var args = _.toArray(arguments); - var context = args.pop(); - _.each(args, function(methodName) { + var context = Array.prototype.pop.call(arguments); + _.each(arguments, function(methodName) { context[methodName] = _.bind(context[methodName], context); }); }; @@ -355,14 +337,14 @@ // Delays a function for the given number of milliseconds, and then calls // it with the arguments supplied. _.delay = function(func, wait) { - var args = _.toArray(arguments).slice(2); + var args = _.rest(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(_.toArray(arguments).slice(1))); + return _.delay.apply(_, [func, 1].concat(_.rest(arguments))); }; // Returns the first function passed as an argument to the second, @@ -524,13 +506,14 @@ /*------------------------------- Aliases ----------------------------------*/ - _.head = _.first; _.forEach = _.each; _.foldl = _.inject = _.reduce; _.foldr = _.reduceRight; _.filter = _.select; _.every = _.all; _.some = _.any; + _.head = _.first; + _.tail = _.rest; _.methods = _.functions; /*------------------------ Setup the OOP Wrapper: --------------------------*/