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. */
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. */
var FUNC_ERROR_TEXT = 'Expected a function';
@@ -6837,6 +6838,45 @@
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.
*
@@ -7000,7 +7040,7 @@
* @name reverse
* @memberOf _
* @category Chain
* @returns {Object} Returns the new reversed `lodash` wrapper instance.
* @returns {Object} Returns the new `lodash` wrapper instance.
* @example
*
* var array = [1, 2, 3];
@@ -13865,7 +13905,7 @@
// Add `LazyWrapper` methods that accept an `iteratee` value.
arrayEach(['filter', 'map', 'takeWhile'], function(methodName, index) {
var type = index + 1,
isFilter = type != LAZY_MAP_FLAG;
isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
LazyWrapper.prototype[methodName] = function(iteratee) {
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() {
return this.filter(identity);
};
@@ -13953,15 +13983,15 @@
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
var checkIteratee = /^(?:filter|find|map|reject)|While$/.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) {
return;
}
lodash.prototype[methodName] = function() {
var args = isTaker ? [1] : arguments,
value = this.__wrapped__,
var value = this.__wrapped__,
args = isTaker ? [1] : arguments,
isLazy = value instanceof LazyWrapper,
iteratee = args[0],
useLazy = isLazy || isArray(value);
@@ -13975,8 +14005,7 @@
// Avoid lazy use if the iteratee has a "length" value other than `1`.
isLazy = useLazy = false;
}
var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined },
chainAll = this.__chain__,
var chainAll = this.__chain__,
isHybrid = !!this.__actions__.length,
isUnwrapped = retUnwrapped && !chainAll,
onlyLazy = isLazy && !isHybrid;
@@ -13984,7 +14013,7 @@
if (!retUnwrapped && useLazy) {
value = onlyLazy ? value : new LazyWrapper(this);
var result = func.apply(value, args);
result.__actions__.push(action);
result.__actions__.push({ 'func': thru, 'args': [interceptor], 'thisArg': undefined });
return new LodashWrapper(result, chainAll);
}
if (isUnwrapped && onlyLazy) {
@@ -14031,6 +14060,7 @@
LazyWrapper.prototype.value = lazyValue;
// Add chaining functions to the `lodash` wrapper.
lodash.prototype.at = wrapperAt;
lodash.prototype.chain = wrapperChain;
lodash.prototype.commit = wrapperCommit;
lodash.prototype.next = wrapperNext;

View File

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