Skip to content

Commit

Permalink
Merge pull request #1901 from thejameskyle/transform
Browse files Browse the repository at this point in the history
Add _.transform method
  • Loading branch information
megawac committed Dec 29, 2014
2 parents fc512c2 + 0bfc402 commit 9924dba
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 0 deletions.
47 changes: 47 additions & 0 deletions test/collections.js
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,53 @@
strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight');
});

test('transform', function() {
var list = _.transform(['foo', 'bar', 'baz'], function(accumulator, value, index){ accumulator[index] = value + '2'; });
deepEqual(list, ['foo2', 'bar2', 'baz2'], 'handles arrays with no accumulator');

list = _.transform(['foo', 'bar', 'baz'], function(accumulator, value, index){ accumulator[index] = value + '2'; }, []);
deepEqual(list, ['foo2', 'bar2', 'baz2'], 'handles arrays with array accumulator');

list = _.transform(['foo', 'bar', 'baz'], function(accumulator, value, index){ accumulator[index] = value + '2'; }, {});
deepEqual(list, {0: 'foo2', 1: 'bar2', 2: 'baz2'}, 'handles arrays with object accumulator');

var obj = _.transform({foo: 1, bar: 2, baz: 3}, function(accumulator, value, key){ accumulator[key] = value + 1; });
deepEqual(obj, {foo: 2, bar: 3, baz: 4}, 'handles objects with no accumulator');

obj = _.transform({foo: 1, bar: 2, baz: 3}, function(accumulator, value, key){ accumulator[key] = value + 1; }, {});
deepEqual(obj, {foo: 2, bar: 3, baz: 4}, 'handles objects with array accumulator');

obj = _.transform({0: 'foo', 1: 'bar', 2: 'baz'}, function(accumulator, value, key){ accumulator[key] = value + '2'; }, []);
deepEqual(obj, ['foo2', 'bar2', 'baz2'], 'handles objects with object accumulator');

function Obj(props) { _.extend(this, props); }
var instance = new Obj({foo: 1, bar: 2, baz: 3});
obj = _.transform(instance, function(accumulator, value, key){ accumulator[key] = value + 1; });
ok(obj !== instance && obj instanceof Obj, 'creates a new instance of the object');
deepEqual(obj, new Obj({foo: 2, bar: 3, baz: 4}), 'handles instances with no accumulator');

var context = {}, actualContext;
_.transform([1], function() { actualContext = this; }, {}, context);
strictEqual(actualContext, context, 'iterates with the correct context');

obj = {foo: 1};
var accumulator = {};
var args = [accumulator, 'foo', obj.foo, obj], actualArgs;
_.transform(obj, function() { actualArgs = _.toArray(args); }, accumulator);
deepEqual(args, actualArgs, 'iterates with the correct arguments');

accumulator = {};
strictEqual(_.transform([1], function() {}, accumulator), accumulator);

deepEqual(_.transform(), {}, 'should return an empty object when no obj or accumulator is passed');

accumulator = [];
strictEqual(_.transform(null, null, accumulator), accumulator, 'should return the accumulator when no obj is passed');

list = _.transform([1, 2, 3, 4], function(accumulator, value) { return value < 3 && accumulator.push(value); });
deepEqual(list, [1, 2], 'transform should stop when false is returned.');
});

test('find', function() {
var array = [1, 2, 3, 4];
strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`');
Expand Down
25 changes: 25 additions & 0 deletions underscore.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,31 @@
return memo;
};

// **Transform** is an alternative to reduce that transforms `obj` to a new
// `accumulator` object.
_.transform = function(obj, iteratee, accumulator, context) {
if (accumulator == null) {
if (_.isArray(obj)) {
accumulator = [];
} else if (_.isObject(obj)) {
var Ctor = obj.constructor;
accumulator = baseCreate(typeof Ctor == 'function' && Ctor.prototype);
} else {
accumulator = {};
}
}
if (obj == null) return accumulator;
iteratee = optimizeCb(iteratee, context, 4);
var keys = obj.length !== +obj.length && _.keys(obj),
length = (keys || obj).length,
index, currentKey;
for (index = 0; index < length; index++) {
currentKey = keys ? keys[index] : index;
if (iteratee(accumulator, obj[currentKey], currentKey, obj) === false) break;
}
return accumulator;
};

// Return the first value which passes a truth test. Aliased as `detect`.
_.find = _.detect = function(obj, predicate, context) {
var key;
Expand Down

0 comments on commit 9924dba

Please sign in to comment.