Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also .

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also .
base repository: jashkenas/underscore
base: 1.1.7
head repository: jashkenas/underscore
compare: 1.2.0
Commits on May 04, 2011
Commits on Jul 15, 2011
Commits on Jul 20, 2011
* Added support for auto-escaping of values using ```<%== ... %>```
Commits on Aug 11, 2011
Commits on Aug 31, 2011
This makes _.last() behave the same as _.first().  Passing an optional
second argument n will return the last n elements of the array.
Allows _.last() to work as expected with _.map().
Showing with 688 additions and 197 deletions.
  1. +143 −83 docs/underscore.html
  2. +107 −17 index.html
  3. +1 −1 package.json
  4. +22 −1 test/arrays.js
  5. +1 −1 test/chaining.js
  6. +14 −1 test/collections.js
  7. +1 −1 test/functions.js
  8. +230 −21 test/objects.js
  9. +5 −1 test/utility.js
  10. +23 −21 underscore-min.js
  11. +141 −49 underscore.js

Large diffs are not rendered by default.

@@ -49,6 +49,14 @@
td {
padding: 2px 12px 2px 0;
}
ul {
list-style-type: circle;
padding: 0 0 0 20px;
}
li {
width: 500px;
margin-bottom: 10px;
}
code, pre, tt {
font-family: Monaco, Consolas, "Lucida Console", monospace;
font-size: 12px;
@@ -118,11 +126,11 @@ <h2>Downloads <i style="padding-left: 12px; font-size:12px;">(Right-click, and u

<table>
<tr>
<td><a href="underscore.js">Development Version (1.1.7)</a></td>
<td><a href="underscore.js">Development Version (1.2.0)</a></td>
<td><i>28kb, Uncompressed with Comments</i></td>
</tr>
<tr>
<td><a href="underscore-min.js">Production Version (1.1.7)</a></td>
<td><a href="underscore-min.js">Production Version (1.2.0)</a></td>
<td><i>3kb, Minified and Gzipped</i></td>
</tr>
</table>
@@ -141,14 +149,15 @@ <h2>Table of Contents</h2>
<a href="#any">any</a>, <a href="#include">include</a>,
<a href="#invoke">invoke</a>, <a href="#pluck">pluck</a>,
<a href="#max">max</a>, <a href="#min">min</a>,
<a href="#sortBy">sortBy</a>, <a href="#groupBy">groupBy</a>, <a href="#sortedIndex">sortedIndex</a>,
<a href="#sortBy">sortBy</a>, <a href="#groupBy">groupBy</a>,
<a href="#sortedIndex">sortedIndex</a>, <a href="#shuffle">shuffle</a>,
<a href="#toArray">toArray</a>, <a href="#size">size</a></span>
</p>

<p>
<b>Arrays</b>
<br />
<span class="methods"><a href="#first">first</a>, <a href="#rest">rest</a>, <a href="#last">last</a>,
<span class="methods"><a href="#first">first</a>, <a href="#initial">initial</a>, <a href="#last">last</a>, <a href="#rest">rest</a>,
<a href="#compact">compact</a>, <a href="#flatten">flatten</a>, <a href="#without">without</a>,
<a href="#union">union</a>, <a href="#intersection">intersection</a>, <a href="#difference">difference</a>,
<a href="#uniq">uniq</a>, <a href="#zip">zip</a>, <a href="#indexOf">indexOf</a>,
@@ -462,6 +471,17 @@ <h2>Collection Functions (Arrays or Objects)</h2>
<pre>
_.sortedIndex([10, 20, 30, 40, 50], 35);
=&gt; 3
</pre>

<p id="shuffle">
<b class="header">shuffle</b><code>_.shuffle(list)</code>
<br />
Returns a shuffled copy of the <b>list</b>, using a version of the
<a href="http://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle">Fisher-Yates shuffle</a>.
</p>
<pre>
_.shuffle([1, 2, 3, 4, 5, 6]);
=&gt; [4, 1, 6, 3, 5, 2]
</pre>

<p id="toArray">
@@ -503,26 +523,39 @@ <h2>Array Functions</h2>
=&gt; 5
</pre>

<p id="rest">
<b class="header">rest</b><code>_.rest(array, [index])</code>
<span class="alias">Alias: <b>tail</b></span>
<p id="initial">
<b class="header">initial</b><code>_.initial(array, [n])</code>
<br />
Returns the <b>rest</b> of the elements in an array. Pass an <b>index</b>
to return the values of the array from that index onward.
Returns everything but the last entry of the array. Especially useful on
the arguments object. Pass <b>n</b> to exclude the last <b>n</b> elements
from the result.
</p>
<pre>
_.rest([5, 4, 3, 2, 1]);
=&gt; [4, 3, 2, 1]
_.initial([5, 4, 3, 2, 1]);
=&gt; [5, 4, 3, 2]
</pre>

<p id="last">
<b class="header">last</b><code>_.last(array)</code>
<b class="header">last</b><code>_.last(array, [n])</code>
<br />
Returns the last element of an <b>array</b>.
Returns the last element of an <b>array</b>. Passing <b>n</b> will return
the last <b>n</b> elements of the array.
</p>
<pre>
_.last([5, 4, 3, 2, 1]);
=&gt; 1
</pre>

<p id="rest">
<b class="header">rest</b><code>_.rest(array, [index])</code>
<span class="alias">Alias: <b>tail</b></span>
<br />
Returns the <b>rest</b> of the elements in an array. Pass an <b>index</b>
to return the values of the array from that index onward.
</p>
<pre>
_.rest([5, 4, 3, 2, 1]);
=&gt; [4, 3, 2, 1]
</pre>

<p id="compact">
@@ -592,12 +625,14 @@ <h2>Array Functions</h2>
</pre>

<p id="uniq">
<b class="header">uniq</b><code>_.uniq(array, [isSorted])</code>
<b class="header">uniq</b><code>_.uniq(array, [isSorted], [iterator])</code>
<span class="alias">Alias: <b>unique</b></span>
<br />
Produces a duplicate-free version of the <b>array</b>, using <i>===</i> to test
object equality. If you know in advance that the <b>array</b> is sorted,
passing <i>true</i> for <b>isSorted</b> will run a much faster algorithm.
If you want to compute unique items based on a transformation, pass an
<b>iterator</b> function.
</p>
<pre>
_.uniq([1, 2, 1, 3, 1, 4]);
@@ -1149,7 +1184,8 @@ <h2>Utility Functions</h2>
for rendering. Useful for rendering complicated bits of HTML from JSON
data sources. Template functions can both interpolate variables, using<br />
<tt>&lt;%= &hellip; %&gt;</tt>, as well as execute arbitrary JavaScript code, with
<tt>&lt;% &hellip; %&gt;</tt>. When you evaluate a template function, pass in a
<tt>&lt;% &hellip; %&gt;</tt>. If you wish to interpolate a value, and have
it be HTML-escaped, use <tt>&lt;%- &hellip; %&gt;</tt> When you evaluate a template function, pass in a
<b>context</b> object that has properties corresponding to the template's free
variables. If you're writing a one-off, you can pass the <b>context</b>
object as the second parameter to <b>template</b> in order to render
@@ -1162,7 +1198,11 @@ <h2>Utility Functions</h2>

var list = "&lt;% _.each(people, function(name) { %&gt; &lt;li&gt;&lt;%= name %&gt;&lt;/li&gt; &lt;% }); %&gt;";
_.template(list, {people : ['moe', 'curly', 'larry']});
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"</pre>
=&gt; "&lt;li&gt;moe&lt;/li&gt;&lt;li&gt;curly&lt;/li&gt;&lt;li&gt;larry&lt;/li&gt;"

var template = _.template("&lt;b&gt;&lt;%- value %&gt;&lt;/b&gt;");
template({value : '&lt;script&gt;'});
=&gt; "&lt;b&gt;&amp;lt;script&amp;gt;&lt;/b&gt;"</pre>

<p>
You can also use <tt>print</tt> from within JavaScript code. This is
@@ -1235,6 +1275,13 @@ <h2 id="duck_typing">Duck Typing</h2>
another type, if you're setting properties with names like "concat" and
"charCodeAt". So be aware.
</p>

<p>
In a similar fashion, <tt>_.each</tt> and all of the other functions
based on it are designed to be able to iterate over any Array-like
JavaScript object, including <tt>arguments</tt>, NodeLists, and more.
Passing hash-like objects with a numeric <tt>length</tt> key won't work.
</p>


<h2>Links &amp; Suggested Reading</h2>
@@ -1247,6 +1294,21 @@ <h2>Links &amp; Suggested Reading</h2>
available on GitHub.
</p>

<p>
<a href="http://brianhaveri.github.com/Underscore.php/">Underscore.php</a>,
a PHP port of the functions that are applicable in both languages.
Includes OOP-wrapping and chaining.
The <a href="http://github.com/brianhaveri/Underscore.php">source</a> is
available on GitHub.
</p>

<p>
<a href="http://vti.github.com/underscore-perl/">Underscore-perl</a>,
a Perl port of many of the Underscore.js functions,
aimed at on Perl hashes and arrays, also
<a href="https://github.com/vti/underscore-perl/">available on GitHub</a>.
</p>

<p>
<a href="https://github.com/edtsech/underscore.string">Underscore.string</a>,
an Underscore extension that adds functions for string-manipulation:
@@ -1280,10 +1342,38 @@ <h2>Links &amp; Suggested Reading</h2>

<h2 id="changelog">Change Log</h2>

<p>
<b class="header">1.2.0</b> &mdash; <small><i>Oct. 5, 2011</i></small><br />
<ul>
<li>
Underscore templates now support HTML escaping interpolations, using
<tt>&lt;%- ... %&gt;</tt> syntax. The <tt>_.isEqual</tt> function now
supports true deep equality comparisons, with checks for cyclic structures,
thanks to Kit Cambridge.
</li>
<li>
Ryan Tenney contributed <tt>_.shuffle</tt>, which uses a modified
Fisher-Yates to give you a shuffled copy of an array.
</li>
<li>
<tt>_.uniq</tt> can now be passed an optional iterator, to determine by
what criteria an object should be considered unique.
</li>
<li>
<tt>_.last</tt> now takes an optional argument which will return the last
N elements of the list.
</li>
<li>
A new <tt>_.initial</tt> function was added, as a mirror of <tt>_.rest</tt>,
which returns all the initial values of a list (except the last N).
</li>
</ul>
</p>

<p>
<b class="header">1.1.7</b> &mdash; <small><i>July 13, 2011</i></small><br />
Added <tt>_.groupBy</tt>, which aggregates a collection into groups of like items.
Added <tt>_.untion</tt> and <tt>_.difference</tt>, to complement the
Added <tt>_.union</tt> and <tt>_.difference</tt>, to complement the
(re-named) <tt>_.intersection</tt>.
Various improvements for support of sparse arrays.
<tt>_.toArray</tt> now returns a clone, if directly passed an array.
@@ -8,5 +8,5 @@
"dependencies" : [],
"repository" : {"type": "git", "url": "git://github.com/documentcloud/underscore.git"},
"main" : "underscore.js",
"version" : "1.1.7"
"version" : "1.2.0"
}
@@ -1,6 +1,6 @@
$(document).ready(function() {

module("Array-only functions (last, compact, uniq, and so on...)");
module("Arrays");

test("arrays: first", function() {
equals(_.first([1,2,3]), 1, 'can pull out the first element of an array');
@@ -24,10 +24,23 @@ $(document).ready(function() {
equals(_.flatten(result).join(','), '2,3,2,3', 'works well with _.map');
});

test("arrays: initial", function() {
equals(_.initial([1,2,3,4,5]).join(", "), "1, 2, 3, 4", 'working initial()');
equals(_.initial([1,2,3,4],2).join(", "), "1, 2", 'initial can take an index');
var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4);
equals(result.join(", "), "1, 2, 3", 'initial works on arguments object');
result = _.map([[1,2,3],[1,2,3]], _.initial);
equals(_.flatten(result).join(','), '1,2,1,2', 'initial works with _.map');
});

test("arrays: last", function() {
equals(_.last([1,2,3]), 3, 'can pull out the last element of an array');
equals(_.last([1,2,3], 0).join(', '), "", 'can pass an index to last');
equals(_.last([1,2,3], 2).join(', '), '2, 3', 'can pass an index to last');
var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4);
equals(result, 4, 'works on an arguments object');
result = _.map([[1,2,3],[1,2,3]], _.last);
equals(result.join(','), '3,3', 'works well with _.map');
});

test("arrays: compact", function() {
@@ -61,6 +74,14 @@ $(document).ready(function() {
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 list = [{name:'moe'}, {name:'curly'}, {name:'larry'}, {name:'curly'}];
var iterator = function(value) { return value.name; };
equals(_.map(_.uniq(list, false, iterator), iterator).join(', '), 'moe, curly, larry', 'can find the unique values of an array using a custom iterator');

var iterator = function(value) { return value +1; };
var list = [1, 2, 2, 3, 4, 4];
equals(_.uniq(list, true, iterator).join(', '), '1, 2, 3, 4', 'iterator works with sorted array');

var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4);
equals(result.join(', '), '1, 2, 3, 4', 'works on an arguments object');
});
@@ -1,6 +1,6 @@
$(document).ready(function() {

module("Underscore chaining.");
module("Chaining");

test("chaining: map/flatten/reduce", function() {
var lyrics = [
@@ -1,6 +1,6 @@
$(document).ready(function() {

module("Collection functions (each, any, select, and so on...)");
module("Collections");

test("collections: each", function() {
_.each([1, 2, 3], function(num, i) {
@@ -177,13 +177,19 @@ $(document).ready(function() {

var neg = _.max([1, 2, 3], function(num){ return -num; });
equals(neg, 1, 'can perform a computation-based max');

equals(-Infinity, _.max({}), 'Maximum value of an empty object');
equals(-Infinity, _.max([]), 'Maximum value of an empty array');
});

test('collections: min', function() {
equals(1, _.min([1, 2, 3]), 'can perform a regular Math.min');

var neg = _.min([1, 2, 3], function(num){ return -num; });
equals(neg, 3, 'can perform a computation-based min');

equals(Infinity, _.min({}), 'Minimum value of an empty object');
equals(Infinity, _.min([]), 'Minimum value of an empty array');
});

test('collections: sortBy', function() {
@@ -204,6 +210,13 @@ $(document).ready(function() {
equals(index, 3, '35 should be inserted at index 3');
});

test('collections: shuffle', function() {
var numbers = _.range(10);
var shuffled = _.shuffle(numbers).sort();
notStrictEqual(numbers, shuffled, 'original object is unmodified');
equals(shuffled.join(','), numbers.join(','), 'contains the same members before and after shuffle');
});

test('collections: toArray', function() {
ok(!_.isArray(arguments), 'arguments object is not an array');
ok(_.isArray(_.toArray(arguments)), 'arguments object converted into array');
@@ -1,6 +1,6 @@
$(document).ready(function() {

module("Function functions (bind, bindAll, and so on...)");
module("Functions");

test("functions: bind", function() {
var context = {name : 'moe'};

Showing you all comments on commits in this comparison.

@fgalassi

This comment has been minimized.

Copy link

@fgalassi fgalassi commented on 03b341d Aug 3, 2011

isSorted is not supposed to be a function, why don't you just change to the ideal signature while leaving the edge case "if second param is not a function use it as isSorted ?

@jdalton

This comment has been minimized.

Copy link
Contributor

@jdalton jdalton commented on 75b2195 Aug 13, 2011

You could replace the length check with something like obj.length == obj.length >>> 0 that way lengths with negative numbers or those out-of-range won't be considered array-like-objects.

@michaelficarra

This comment has been minimized.

Copy link
Collaborator

@michaelficarra michaelficarra commented on 1facc0e Oct 4, 2011

I thought we wanted these early bail-outs. If we can easily tell that a deep comparison will not produce true, we should return false here.

// Invoke a custom `isEqual` method if one is provided.
if (_.isFunction(a.isEqual)) return a.isEqual(b);
// If only `b` provides an `isEqual` method, `a` and `b` are not equal.
if (_.isFunction(b.isEqual)) return false;
@jashkenas

This comment has been minimized.

Copy link
Owner Author

@jashkenas jashkenas commented on 1facc0e Oct 4, 2011

We do, which is why the rest of them are in. If a does not have an isEqual method, then it is highly unlikely that b will. It's just a random property check at that point.

@michaelficarra

This comment has been minimized.

Copy link
Collaborator

@michaelficarra michaelficarra commented on 1facc0e Oct 4, 2011

Ah, alright, I'm convinced.