Ensure _.at works with an out of range index when chaining.

This commit is contained in:
John-David Dalton
2015-11-15 00:00:07 -08:00
parent 45f07d5014
commit cad0fbfc4d
2 changed files with 59 additions and 26 deletions

View File

@@ -43,7 +43,8 @@
/** Used to indicate the type of lazy iteratees. */ /** Used to indicate the type of lazy iteratees. */
var LAZY_FILTER_FLAG = 1, var LAZY_FILTER_FLAG = 1,
LAZY_MAP_FLAG = 2; LAZY_MAP_FLAG = 2,
LAZY_WHILE_FLAG = 3;
/** Used as the `TypeError` message for "Functions" methods. */ /** Used as the `TypeError` message for "Functions" methods. */
var FUNC_ERROR_TEXT = 'Expected a function'; var FUNC_ERROR_TEXT = 'Expected a function';
@@ -6837,6 +6838,45 @@
return interceptor(value); return interceptor(value);
} }
/**
* Creates a wrapped array of values corresponding to `paths` of the wrapped object.
*
* @name at
* @memberOf _
* @category Chain
* @param {...(string|string[])} [paths] The property paths of elements to pick,
* specified individually or in arrays.
* @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
* var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
*
* _(object).at(['a[0].b.c', 'a[1]']);
* // => [3, 4]
*
* _(['a', 'b', 'c']).at(0, 2);
* // => ['a', 'c']
*/
var wrapperAt = rest(function(paths) {
paths = baseFlatten(paths);
var length = paths.length,
start = length ? paths[0] : 0,
value = this.__wrapped__,
interceptor = function(object) { return baseAt(object, paths); };
if (length > 1 || this.__actions__.length || !(value instanceof LazyWrapper) || !isIndex(start)) {
return this.thru(interceptor);
}
value = value.slice(start, +start + (length ? 1 : 0));
value.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
return new LodashWrapper(value, this.__chain__).thru(function(object) {
if (length && !object.length) {
object.push(undefined);
}
return object;
});
});
/** /**
* Enables explicit method chaining on the wrapper object. * Enables explicit method chaining on the wrapper object.
* *
@@ -7000,7 +7040,7 @@
* @name reverse * @name reverse
* @memberOf _ * @memberOf _
* @category Chain * @category Chain
* @returns {Object} Returns the new reversed `lodash` wrapper instance. * @returns {Object} Returns the new `lodash` wrapper instance.
* @example * @example
* *
* var array = [1, 2, 3]; * var array = [1, 2, 3];
@@ -13865,7 +13905,7 @@
// Add `LazyWrapper` methods that accept an `iteratee` value. // Add `LazyWrapper` methods that accept an `iteratee` value.
arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) { arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
var type = index + 1, var type = index + 1,
isFilter = type != LAZY_MAP_FLAG; isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
LazyWrapper.prototype[methodName] = function(iteratee) { LazyWrapper.prototype[methodName] = function(iteratee) {
var result = this.clone(); var result = this.clone();
@@ -13893,16 +13933,6 @@
}; };
}); });
LazyWrapper.prototype.at = rest(function(paths) {
paths = baseFlatten(paths);
var length = paths.length,
start = length ? paths[0] : 0;
return (length < 2 && isIndex(start))
? this.slice(start, length ? (+start + 1) : start)
: new LazyWrapper(this);
});
LazyWrapper.prototype.compact = function() { LazyWrapper.prototype.compact = function() {
return this.filter(identity); return this.filter(identity);
}; };
@@ -13953,15 +13983,15 @@
baseForOwn(LazyWrapper.prototype, function(func, methodName) { baseForOwn(LazyWrapper.prototype, function(func, methodName) {
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName), var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName),
isTaker = /^(?:head|last)$/.test(methodName), isTaker = /^(?:head|last)$/.test(methodName),
retUnwrapped = isTaker || /^find/.test(methodName), lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName],
lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName]; retUnwrapped = isTaker || /^find/.test(methodName);
if (!lodashFunc) { if (!lodashFunc) {
return; return;
} }
lodash.prototype[methodName] = function() { lodash.prototype[methodName] = function() {
var args = isTaker ? [1] : arguments, var value = this.__wrapped__,
value = this.__wrapped__, args = isTaker ? [1] : arguments,
isLazy = value instanceof LazyWrapper, isLazy = value instanceof LazyWrapper,
iteratee = args[0], iteratee = args[0],
useLazy = isLazy || isArray(value); useLazy = isLazy || isArray(value);
@@ -13975,8 +14005,7 @@
// Avoid lazy use if the iteratee has a "length" value other than `1`. // Avoid lazy use if the iteratee has a "length" value other than `1`.
isLazy = useLazy = false; isLazy = useLazy = false;
} }
var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined }, var chainAll = this.__chain__,
chainAll = this.__chain__,
isHybrid = !!this.__actions__.length, isHybrid = !!this.__actions__.length,
isUnwrapped = retUnwrapped && !chainAll, isUnwrapped = retUnwrapped && !chainAll,
onlyLazy = isLazy && !isHybrid; onlyLazy = isLazy && !isHybrid;
@@ -13984,7 +14013,7 @@
if (!retUnwrapped && useLazy) { if (!retUnwrapped && useLazy) {
value = onlyLazy ? value : new LazyWrapper(this); value = onlyLazy ? value : new LazyWrapper(this);
var result = func.apply(value, args); var result = func.apply(value, args);
result.__actions__.push(action); result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
return new LodashWrapper(result, chainAll); return new LodashWrapper(result, chainAll);
} }
if (isUnwrapped && onlyLazy) { if (isUnwrapped && onlyLazy) {
@@ -14031,6 +14060,7 @@
LazyWrapper.prototype.value = lazyValue; LazyWrapper.prototype.value = lazyValue;
// Add chaining functions to the `lodash` wrapper. // Add chaining functions to the `lodash` wrapper.
lodash.prototype.at = wrapperAt;
lodash.prototype.chain = wrapperChain; lodash.prototype.chain = wrapperChain;
lodash.prototype.commit = wrapperCommit; lodash.prototype.commit = wrapperCommit;
lodash.prototype.next = wrapperNext; lodash.prototype.next = wrapperNext;

View File

@@ -1269,7 +1269,7 @@
}); });
QUnit.test('should support shortcut fusion', function(assert) { QUnit.test('should support shortcut fusion', function(assert) {
assert.expect(6); assert.expect(8);
if (!isNpm) { if (!isNpm) {
var array = lodashStable.range(LARGE_ARRAY_SIZE), var array = lodashStable.range(LARGE_ARRAY_SIZE),
@@ -1277,16 +1277,19 @@
iteratee = function(value) { count++; return square(value); }, iteratee = function(value) { count++; return square(value); },
lastIndex = LARGE_ARRAY_SIZE - 1; lastIndex = LARGE_ARRAY_SIZE - 1;
_.each([lastIndex, lastIndex + '', []], function(n, index) { _.each([lastIndex, lastIndex + '', LARGE_ARRAY_SIZE, []], function(n, index) {
count = 0; count = 0;
var actual = _(array).map(iteratee).at(n).value(); var actual = _(array).map(iteratee).at(n).value(),
expected = index < 2 ? 1 : 0;
assert.strictEqual(count, index == 2 ? 0 : 1); assert.strictEqual(count, expected);
assert.deepEqual(actual, index == 2 ? [] : [square(lastIndex)]);
expected = index == 3 ? [] : [index == 2 ? undefined : square(lastIndex)];
assert.deepEqual(actual, expected);
}); });
} }
else { else {
skipTest(assert, 6); skipTest(assert, 8);
} }
}); });