diff --git a/index.html b/index.html
index ebbf82dbc..9850daf9d 100644
--- a/index.html
+++ b/index.html
@@ -123,10 +123,14 @@
Collections
each, map,
- reduce, detect, select, reject, all,
- any, include, invoke, pluck, max,
- min, sortBy, sortedIndex, toArray,
- size
+ reduce, reduceRight,
+ detect, select,
+ reject, all,
+ any, include,
+ invoke, pluck,
+ max, min,
+ sortBy, sortedIndex,
+ toArray, size
@@ -195,7 +199,7 @@ _.map([1, 2, 3], function(num){ return num * 3 });
reduce_.reduce(list, memo, iterator, [context])
- Alias: inject
+ 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
@@ -205,6 +209,19 @@ _.map([1, 2, 3], function(num){ return num * 3 });
var sum = _.reduce([1, 2, 3], 0, function(memo, num){ return memo + num });
=> 6
+
+
+
+ reduceRight_.reduceRight(list, memo, iterator, [context])
+ Alias: foldr
+
+ The right-associative version of reduce. Delegates to the
+ JavaScript 1.8 version of reduceRight, if it exists.
+
+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]
diff --git a/test/collections.js b/test/collections.js index 3ef2ccba3..a55d02e88 100644 --- a/test/collections.js +++ b/test/collections.js @@ -47,10 +47,19 @@ $(document).ready(function() { var sum = _.reduce([1, 2, 3], 0, function(sum, num){ return sum + num; }); 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); + equals(sum, 18, 'can reduce with a context object'); + sum = _.inject([1, 2, 3], 0, function(sum, num){ return sum + num; }); equals(sum, 6, 'aliased as "inject"'); }); + test('collections: reduceRight', function() { + var list = _.foldr([1, 2, 3], '', function(memo, num){ return memo + num; }); + equals(list, '321', 'can perform right folds'); + }); + test('collections: detect', function() { var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; }); equals(result, 2, 'found the first "2" and broke the loop'); diff --git a/underscore.js b/underscore.js index fee281f8d..9b2dfb84b 100644 --- a/underscore.js +++ b/underscore.js @@ -61,14 +61,26 @@ }; // Reduce builds up a single result from a list of values. Also known as - // inject, or foldl. + // inject, or foldl. Uses JavaScript 1.8's version of reduce, if possible. _.reduce = function(obj, memo, iterator, context) { + if (obj && obj.reduce) return obj.reduce(_.bind(iterator, context), memo); _.each(obj, function(value, index, list) { memo = iterator.call(context, memo, value, index, list); }); return memo; }; + // The right-associative version of reduce, also known as foldr. Uses + // JavaScript 1.8's version of reduceRight, if available. + _.reduceRight = function(obj, memo, iterator, context) { + if (obj && obj.reduceRight) return obj.reduceRight(_.bind(iterator, context), memo); + var reversed = _.clone(_.toArray(obj)).reverse(); + _.each(reversed, function(value, index) { + memo = iterator.call(context, memo, value, index, obj); + }); + return memo; + }; + // Return the first value which passes a truth test. _.detect = function(obj, iterator, context) { var result; @@ -368,6 +380,7 @@ // Create a (shallow-cloned) duplicate of an object. _.clone = function(obj) { + if (_.isArray(obj)) return obj.slice(0); return _.extend({}, obj); }; @@ -455,7 +468,8 @@ /*------------------------------- Aliases ----------------------------------*/ _.forEach = _.each; - _.inject = _.reduce; + _.foldl = _.inject = _.reduce; + _.foldr = _.reduceRight; _.filter = _.select; _.every = _.all; _.some = _.any;