Add _.propertyOf.

This commit is contained in:
John-David Dalton
2014-11-06 23:39:39 -08:00
parent f0a2c88fdc
commit 2f3afd3637
2 changed files with 159 additions and 47 deletions

View File

@@ -1011,12 +1011,12 @@
* `initial`, `intersection`, `invert`, `invoke`, `keys`, `keysIn`, `map`, * `initial`, `intersection`, `invert`, `invoke`, `keys`, `keysIn`, `map`,
* `mapValues`, `matches`, `memoize`, `merge`, `mixin`, `negate`, `noop`, * `mapValues`, `matches`, `memoize`, `merge`, `mixin`, `negate`, `noop`,
* `omit`, `once`, `pairs`, `partial`, `partialRight`, `partition`, `pick`, * `omit`, `once`, `pairs`, `partial`, `partialRight`, `partition`, `pick`,
* `pluck`, `property`, `pull`, `pullAt`, `push`, `range`, `rearg`, `reject`, * `pluck`, `property`, `propertyOf`, `pull`, `pullAt`, `push`, `range`,
* `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, * `rearg`, `reject`, `remove`, `rest`, `reverse`, `shuffle`, `slice`, `sort`,
* `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`, * `sortBy`, `splice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`,
* `thru`, `times`, `toArray`, `transform`, `union`, `uniq`, `unshift`, * `tap`, `throttle`, `thru`, `times`, `toArray`, `transform`, `union`, `uniq`,
* `unzip`, `values`, `valuesIn`, `where`, `without`, `wrap`, `xor`, `zip`, * `unshift`, `unzip`, `values`, `valuesIn`, `where`, `without`, `wrap`, `xor`,
* and `zipObject` * `zip`, and `zipObject`
* *
* The non-chainable wrapper functions are: * The non-chainable wrapper functions are:
* `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `contains`, * `attempt`, `camelCase`, `capitalize`, `clone`, `cloneDeep`, `contains`,
@@ -9561,8 +9561,8 @@
} }
/** /**
* Creates a "_.pluck" style function which returns the `key` value of a * Creates a "_.pluck" style function which returns the value associated with
* given object. * the `key` of a given object.
* *
* @static * @static
* @memberOf _ * @memberOf _
@@ -9585,11 +9585,37 @@
* // => [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }] * // => [{ 'user': 'barney', 'age': 36 }, { 'user': 'fred', 'age': 40 }]
*/ */
function property(key) { function property(key) {
key = String(key);
return function(object) { return function(object) {
return object == null ? undefined : object[key]; return object == null ? undefined : object[key];
}; };
} }
/**
* The inverse of `_.property`; this method creates a function which returns
* the value associated with a given key of `object`.
*
* @static
* @memberOf _
* @category Utility
* @param {Object} object The object to inspect.
* @returns {Function} Returns the new function.
* @example
*
* var fred = { 'user': 'fred', 'age': 40, 'active': true };
* _.map(['age', 'active', _.propertyOf(fred));
* // => [40, true]
*
* var object = { 'a': 3, 'b': 1, 'c': 2 };
* _.sortBy(['a', 'b', 'c'], _.propertyOf(object));
* // => ['b', 'c', 'a']
*/
function propertyOf(object) {
return function(key) {
return object == null ? undefined : object[key];
};
}
/** /**
* Produces a random number between `min` and `max` (inclusive). If only one * Produces a random number between `min` and `max` (inclusive). If only one
* argument is provided a number between `0` and the given number is returned. * argument is provided a number between `0` and the given number is returned.
@@ -9887,6 +9913,7 @@
lodash.pick = pick; lodash.pick = pick;
lodash.pluck = pluck; lodash.pluck = pluck;
lodash.property = property; lodash.property = property;
lodash.propertyOf = propertyOf;
lodash.pull = pull; lodash.pull = pull;
lodash.pullAt = pullAt; lodash.pullAt = pullAt;
lodash.range = range; lodash.range = range;

View File

@@ -928,17 +928,22 @@
QUnit.module('lodash.at'); QUnit.module('lodash.at');
(function() { (function() {
var args = arguments; var args = arguments,
array = ['a', 'b', 'c'];
array['1.1'] = array['-1'] = 1;
test('should return the elements corresponding to the specified keys', 1, function() {
var actual = _.at(array, [0, 2]);
deepEqual(actual, ['a', 'c']);
});
test('should return `undefined` for nonexistent keys', 1, function() { test('should return `undefined` for nonexistent keys', 1, function() {
var actual = _.at(['a', 'b', 'c'], [2, 4, 0]); var actual = _.at(array, [2, 4, 0]);
deepEqual(actual, ['c', undefined, 'a']); deepEqual(actual, ['c', undefined, 'a']);
}); });
test('should use `undefined` for non-index keys on array-like values', 1, function() { test('should use `undefined` for non-index keys on array-like values', 1, function() {
var array = ['a', 'b', 'c'];
array['1.1'] = array['-1'] = 1;
var values = _.reject(empties, function(value) { var values = _.reject(empties, function(value) {
return value === 0 || _.isArray(value); return value === 0 || _.isArray(value);
}).concat(-1, 1.1); }).concat(-1, 1.1);
@@ -949,8 +954,9 @@
deepEqual(actual, expected); deepEqual(actual, expected);
}); });
test('should return an empty array when no keys are provided', 1, function() { test('should return an empty array when no keys are provided', 2, function() {
deepEqual(_.at(['a', 'b', 'c']), []); deepEqual(_.at(array), []);
deepEqual(_.at(array, [], []), []);
}); });
test('should accept multiple key arguments', 1, function() { test('should accept multiple key arguments', 1, function() {
@@ -958,7 +964,7 @@
deepEqual(actual, ['d', 'a', 'c']); deepEqual(actual, ['d', 'a', 'c']);
}); });
test('should work with a falsey `array` argument when keys are provided', 1, function() { test('should work with a falsey `collection` argument when keys are provided', 1, function() {
var expected = _.map(falsey, _.constant([undefined, undefined])); var expected = _.map(falsey, _.constant([undefined, undefined]));
var actual = _.map(falsey, function(value) { var actual = _.map(falsey, function(value) {
@@ -972,7 +978,12 @@
test('should work with an `arguments` object for `collection`', 1, function() { test('should work with an `arguments` object for `collection`', 1, function() {
var actual = _.at(args, [2, 0]); var actual = _.at(args, [2, 0]);
deepEqual(actual, ['c', 'a']); deepEqual(actual, [3, 1]);
});
test('should work with `arguments` object as secondary arguments', 1, function() {
var actual = _.at([1, 2, 3, 4, 5], args);
deepEqual(actual, [2, 3, 4]);
}); });
test('should work with an object for `collection`', 1, function() { test('should work with an object for `collection`', 1, function() {
@@ -980,6 +991,14 @@
deepEqual(actual, [3, 1]); deepEqual(actual, [3, 1]);
}); });
test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var actual = _.at(new Foo, 'b');
deepEqual(actual, [2]);
});
_.each({ _.each({
'literal': 'abc', 'literal': 'abc',
'object': Object('abc') 'object': Object('abc')
@@ -989,7 +1008,7 @@
deepEqual(_.at(collection, [2, 0]), ['c', 'a']); deepEqual(_.at(collection, [2, 0]), ['c', 'a']);
}); });
}); });
}('a', 'b', 'c')); }(1, 2, 3));
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -4922,7 +4941,7 @@
test('should include inherited functions', 1, function() { test('should include inherited functions', 1, function() {
function Foo() { function Foo() {
this.a = _.identity; this.a = _.identity;
this.b = 'b' this.b = 'b';
} }
Foo.prototype.c = _.noop; Foo.prototype.c = _.noop;
deepEqual(_.functions(new Foo).sort(), ['a', 'c']); deepEqual(_.functions(new Foo).sort(), ['a', 'c']);
@@ -5388,6 +5407,7 @@
test('should only add multiple values to own, not inherited, properties', 2, function() { test('should only add multiple values to own, not inherited, properties', 2, function() {
var object = { 'a': 'hasOwnProperty', 'b': 'constructor' }; var object = { 'a': 'hasOwnProperty', 'b': 'constructor' };
deepEqual(_.invert(object), { 'hasOwnProperty': 'a', 'constructor': 'b' }); deepEqual(_.invert(object), { 'hasOwnProperty': 'a', 'constructor': 'b' });
ok(_.isEqual(_.invert(object, true), { 'hasOwnProperty': ['a'], 'constructor': ['b'] })); ok(_.isEqual(_.invert(object, true), { 'hasOwnProperty': ['a'], 'constructor': ['b'] }));
}); });
@@ -7298,13 +7318,10 @@
}); });
test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() { test('`_.' + methodName + '` should ' + (isKeys ? 'not' : '') + ' include inherited properties', 1, function() {
function Foo() { function Foo() { this.a = 1; }
this.a = 1; Foo.prototype.b = 2;
this.b = 2;
}
Foo.prototype.c = 3;
var expected = isKeys ? ['a', 'b'] : ['a', 'b', 'c']; var expected = isKeys ? ['a'] : ['a', 'b'];
deepEqual(func(new Foo).sort(), expected); deepEqual(func(new Foo).sort(), expected);
}); });
}); });
@@ -7776,14 +7793,12 @@
}); });
test('should not match by inherited `source` properties', 1, function() { test('should not match by inherited `source` properties', 1, function() {
function Foo() {} function Foo() { this.a = 1; }
Foo.prototype = { 'b': 2 }; Foo.prototype.b = 2;
var source = new Foo;
source.a = 1;
var objects = [{ 'a': 1 }, { 'a': 1, 'b': 2 }], var objects = [{ 'a': 1 }, { 'a': 1, 'b': 2 }],
expected = _.map(objects, _.constant(true)), expected = _.map(objects, _.constant(true)),
source = new Foo,
matches = _.matches(source), matches = _.matches(source),
actual = _.map(objects, matches); actual = _.map(objects, matches);
@@ -8330,7 +8345,7 @@
test('should not assign inherited `source` properties', 1, function() { test('should not assign inherited `source` properties', 1, function() {
function Foo() {} function Foo() {}
Foo.prototype = { 'a': _.noop }; Foo.prototype.a = _.noop;
deepEqual(_.mixin({}, new Foo, {}), {}); deepEqual(_.mixin({}, new Foo, {}), {});
}); });
@@ -9286,10 +9301,15 @@
(function() { (function() {
test('should return an array of property values from each element of a collection', 1, function() { test('should return an array of property values from each element of a collection', 1, function() {
var objects = [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }], var objects = [{ 'name': 'barney', 'age': 36 }, { 'name': 'fred', 'age': 40 }];
actual = _.pluck(objects, 'name'); deepEqual(_.pluck(objects, 'name'), ['barney', 'fred']);
});
deepEqual(actual, ['barney', 'fred']); test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
deepEqual(_.pluck([new Foo], 'b'), [2]);
}); });
test('should work with an object for `collection`', 1, function() { test('should work with an object for `collection`', 1, function() {
@@ -9324,20 +9344,88 @@
(function() { (function() {
test('should create a function that plucks a property value of a given object', 3, function() { test('should create a function that plucks a property value of a given object', 3, function() {
var object = { 'a': 1, 'b': 2 }, var object = { 'a': 1, 'b': 2 },
property = _.property('a'); prop = _.property('a');
strictEqual(property.length, 1); strictEqual(prop.length, 1);
strictEqual(property(object), 1); strictEqual(prop(object), 1);
property = _.property('b'); prop = _.property('b');
strictEqual(property(object), 2); strictEqual(prop(object), 2);
}); });
test('should work with non-string `prop` arguments', 1, function() { test('should work with non-string `prop` arguments', 1, function() {
var array = [1, 2, 3], var prop = _.property(1);
property = _.property(1); strictEqual(prop([1, 2, 3]), 2);
});
strictEqual(property(array), 2); test('should coerce key to a string', 1, function() {
function fn() {}
fn.toString = _.constant('fn');
var objects = [{ 'null': 1 }, { 'undefined': 2 }, { 'fn': 3 }, { '[object Object]': 4 }],
values = [null, undefined, fn, {}]
var actual = _.map(objects, function(object, index) {
var prop = _.property(values[index]);
return prop(object);
});
deepEqual(actual, [1, 2, 3, 4]);
});
test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var prop = _.property('b');
strictEqual(prop(new Foo), 2);
});
test('should work when `object` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
var actual = _.map(values, function(value, index) {
var prop = _.property('a');
return index ? prop(value) : prop();
});
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.propertyOf');
(function() {
test('should create a function that plucks a property value of a given key', 3, function() {
var object = { 'a': 1, 'b': 2 },
propOf = _.propertyOf(object);
strictEqual(propOf.length, 1);
strictEqual(propOf('a'), 1);
strictEqual(propOf('b'), 2);
});
test('should pluck inherited property values', 1, function() {
function Foo() { this.a = 1; }
Foo.prototype.b = 2;
var propOf = _.propertyOf(new Foo);
strictEqual(propOf('b'), 2);
});
test('should work when `object` is nullish', 1, function() {
var values = [, null, undefined],
expected = _.map(values, _.constant(undefined));
var actual = _.map(values, function(value, index) {
var propOf = index ? _.propertyOf(value) : _.propertyOf();
return propOf('a');
});
deepEqual(actual, expected);
}); });
}()); }());
@@ -13141,14 +13229,11 @@
var args = arguments, var args = arguments,
array = [1, 2, 3, 4, 5, 6]; array = [1, 2, 3, 4, 5, 6];
test('should work with `arguments` objects', 31, function() { test('should work with `arguments` objects', 29, function() {
function message(methodName) { function message(methodName) {
return '`_.' + methodName + '` should work with `arguments` objects'; return '`_.' + methodName + '` should work with `arguments` objects';
} }
deepEqual(_.at(args, 0, 4), [1, 5], message('at'));
deepEqual(_.at(array, args), [2, undefined, 4, undefined, 6], '_.at should work with `arguments` objects as secondary arguments');
deepEqual(_.difference(args, [null]), [1, [3], 5], message('difference')); deepEqual(_.difference(args, [null]), [1, [3], 5], message('difference'));
deepEqual(_.difference(array, args), [2, 3, 4, 6], '_.difference should work with `arguments` objects as secondary arguments'); deepEqual(_.difference(array, args), [2, 3, 4, 6], '_.difference should work with `arguments` objects as secondary arguments');
@@ -13319,7 +13404,7 @@
var acceptFalsey = _.difference(allMethods, rejectFalsey); var acceptFalsey = _.difference(allMethods, rejectFalsey);
test('should accept falsey arguments', 198, function() { test('should accept falsey arguments', 199, function() {
var emptyArrays = _.map(falsey, _.constant([])), var emptyArrays = _.map(falsey, _.constant([])),
isExposed = '_' in root, isExposed = '_' in root,
oldDash = root._; oldDash = root._;