From 86e6dd58c2817b40a1249e452cc915f3375e79a5 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 18 Nov 2009 16:11:35 -0500 Subject: [PATCH 1/5] code-ify stopIteration in the docs --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index f0bc7a335..ff1a6dc9e 100644 --- a/index.html +++ b/index.html @@ -920,7 +920,7 @@

Change Log

0.4.3
- Started using the native StopIteration object in browsers that support it. + Started using the native StopIteration object in browsers that support it. Fixed Underscore setup for CommonJS environments.

From 8cac2d5bd75c606fa987a97a97213e48974ed7f0 Mon Sep 17 00:00:00 2001 From: Luke Sutton Date: Thu, 19 Nov 2009 11:07:14 +1030 Subject: [PATCH 2/5] Add init(), tail() and reverse() Array functions. Alias first() to head(); --- test/arrays.js | 15 +++++++++++++++ underscore.js | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/test/arrays.js b/test/arrays.js index d069d9d62..a9dcb4e90 100644 --- a/test/arrays.js +++ b/test/arrays.js @@ -58,4 +58,19 @@ $(document).ready(function() { equals(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element'); }); + 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/underscore.js b/underscore.js index ac1b8659d..ad6dbecac 100644 --- a/underscore.js +++ b/underscore.js @@ -305,6 +305,29 @@ 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: -----------------------------*/ @@ -501,6 +524,7 @@ /*------------------------------- Aliases ----------------------------------*/ + _.head = _.first; _.forEach = _.each; _.foldl = _.inject = _.reduce; _.foldr = _.reduceRight; From 4ed79d5f778e8e8eb5fa0232545ac719cf4c9d42 Mon Sep 17 00:00:00 2001 From: Luke Sutton Date: Thu, 19 Nov 2009 11:35:21 +1030 Subject: [PATCH 3/5] Correct the test for functions() to account for the new functions and aliases. --- test/utility.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/utility.js b/test/utility.js index ed6ee120b..0acc6a16d 100644 --- a/test/utility.js +++ b/test/utility.js @@ -31,13 +31,13 @@ $(document).ready(function() { }); test("utility: functions", function() { - var expected = ["all", "any", "bind", "bindAll", "breakLoop", "clone", "compact", "compose", - "defer", "delay", "detect", "each", "every", "extend", "filter", "first", - "flatten", "foldl", "foldr", "forEach", "functions", "identity", "include", - "indexOf", "inject", "intersect", "invoke", "isArray", "isElement", "isEmpty", "isEqual", + 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", "isFunction", "isNumber", "isString", "isUndefined", "keys", "last", "lastIndexOf", "map", "max", - "methods", "min", "pluck", "reduce", "reduceRight", "reject", "select", - "size", "some", "sortBy", "sortedIndex", "template", "toArray", "uniq", + "methods", "min", "pluck", "reduce", "reduceRight", "reject", "reverse", "select", + "size", "some", "sortBy", "sortedIndex", "tail", "template", "toArray", "uniq", "uniqueId", "values", "without", "wrap", "zip"]; ok(_(expected).isEqual(_.methods()), 'provides a sorted list of functions'); }); From f8e939d30ac53ba672e0f94a20f680d2b6badcfa Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Wed, 18 Nov 2009 21:54:50 -0500 Subject: [PATCH 4/5] fixing template test validation for IE --- test/utility.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/utility.js b/test/utility.js index ed6ee120b..16cb01013 100644 --- a/test/utility.js +++ b/test/utility.js @@ -46,9 +46,9 @@ $(document).ready(function() { var basicTemplate = _.template("<%= thing %> is gettin' on my noives!"); var result = basicTemplate({thing : 'This'}); equals(result, "This is gettin' on my noives!", 'can do basic attribute interpolation'); - var fancyTemplate = _.template("<% for (key in people) { %>
  • <%= people[key] %>
  • <% } %>"); + var fancyTemplate = _.template("
      <% for (key in people) { %>
    • <%= people[key] %>
    • <% } %>
    "); result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); - equals(result, "
  • Moe
  • Larry
  • Curly
  • ", 'can run arbitrary javascript in templates'); + equals(result, "
    • Moe
    • Larry
    • Curly
    ", 'can run arbitrary javascript in templates'); }); }); From ae968a6ea06dd4fd6f5184b3e6d14f9a077b3d01 Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Thu, 19 Nov 2009 09:37:56 -0500 Subject: [PATCH 5/5] Underscore 0.4.5, with first/rest, head/tail, and all Array functions guaranteed to work on 'arguments' objects. Many method implementations reworked to use _.rest() --- index.html | 43 +++++++++++++++++++++++------ test/arrays.js | 69 ++++++++++++++++++++++++++++------------------- test/utility.js | 6 ++--- underscore-min.js | 22 +++++++-------- underscore.js | 63 ++++++++++++++++--------------------------- 5 files changed, 113 insertions(+), 90 deletions(-) diff --git a/index.html b/index.html index ff1a6dc9e..c2b1ac241 100644 --- a/index.html +++ b/index.html @@ -107,11 +107,11 @@

    Downloads (Right-click, and u

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

    Table of Contents

    Arrays
    - first, last, + first, rest, last, compact, flatten, without, uniq, intersect, zip, indexOf, lastIndexOf @@ -454,15 +454,33 @@

    Collection Functions (Arrays or Objects)

    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 @@

    Array Functions

    Returns the last element of an array.

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

    Chaining

    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: --------------------------*/