diff --git a/lib/utils/helpers.js b/lib/utils/helpers.js index e442922..78fd016 100644 --- a/lib/utils/helpers.js +++ b/lib/utils/helpers.js @@ -136,11 +136,42 @@ function invoke(collection, fn) { return vals; } +function reduce(collection, iteratee, accumulator, thisVal) { + var isArray = H.isArray(collection); + + if (!isArray && !H.isObjectLoose(collection)) { + return null; + } + + if (iteratee == null) { + iteratee = identity; + } + + if (accumulator == null) { + if (isArray) { + accumulator = collection.shift(); + } else { + for (var key in collection) { + accumulator = collection[key]; + delete collection[key]; + break; + } + } + } + + iterate(collection, function(object, key) { + accumulator = iteratee.call(thisVal, accumulator, object, key); + }); + + return accumulator; +} + extend(H, { pluck: pluck, each: iterate, map: map, - invoke: invoke + invoke: invoke, + reduce: reduce }); function arity(fn, n) { diff --git a/spec/lib/utils/helpers.spec.js b/spec/lib/utils/helpers.spec.js index afa26d8..4ee7b12 100644 --- a/spec/lib/utils/helpers.spec.js +++ b/spec/lib/utils/helpers.spec.js @@ -212,6 +212,45 @@ describe("Helpers", function() { }); }); + describe("#reduce", function() { + var arr = [1, 2, 3, 4, 5, 6], + obj = { a: 1, b: 2 }; + + function add(sum, n) { return sum + n; } + + it("reduces over a collection with the provided iteratee", function() { + expect(_.reduce(arr, add, 0)).to.be.eql(21); + expect(_.reduce(obj, add, 0)).to.be.eql(3); + }); + + it("defaults to the first value for the accumulator", function() { + var obj = { + a: { name: "hello" }, + b: { name: "world" } + }; + + expect(_.reduce(arr, add)).to.be.eql(21); + expect( + _.reduce(obj, function(acc, val) { + acc.name += " " + val.name; + return acc; + }) + ).to.be.eql({ name: "hello world"}); + }); + + it("supports providing a `this` value", function() { + var self = { + toString: function(y) { return y.toString(); } + }; + + var fn = function(acc, val) { + return acc + this.toString(val); + }; + + expect(_.reduce(arr, fn, 1, self)).to.be.eql("123456"); + }); + }); + describe("#arity", function() { it("creates a function that only takes a certain # of args", function() { var fn = spy();