Allow placeholders to persist through more than 1 curried call.

This commit is contained in:
John-David Dalton
2016-02-20 16:53:08 -08:00
parent d64583b743
commit 2b8b63e59f
2 changed files with 83 additions and 32 deletions

View File

@@ -1032,6 +1032,26 @@
return object.index - other.index; return object.index - other.index;
} }
/**
* Gets the number of `placeholder` occurrences in `array`.
*
* @private
* @param {Array} array The array to inspect.
* @param {*} placeholder The placeholder to search for.
* @returns {number} Returns the placeholder count.
*/
function countHolders(array, placeholder) {
var length = array.length,
result = 0;
while (length--) {
if (array[length] === placeholder) {
result++;
}
}
return result;
}
/** /**
* Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters. * Used by `_.deburr` to convert latin-1 supplementary letters to basic latin letters.
* *
@@ -1170,7 +1190,8 @@
result = []; result = [];
while (++index < length) { while (++index < length) {
if (array[index] === placeholder) { var value = array[index];
if (value === placeholder || value === PLACEHOLDER) {
array[index] = PLACEHOLDER; array[index] = PLACEHOLDER;
result[++resIndex] = index; result[++resIndex] = index;
} }
@@ -3870,23 +3891,28 @@
* @param {Array|Object} args The provided arguments. * @param {Array|Object} args The provided arguments.
* @param {Array} partials The arguments to prepend to those provided. * @param {Array} partials The arguments to prepend to those provided.
* @param {Array} holders The `partials` placeholder indexes. * @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
* @returns {Array} Returns the new array of composed arguments. * @returns {Array} Returns the new array of composed arguments.
*/ */
function composeArgs(args, partials, holders) { function composeArgs(args, partials, holders, isCurried) {
var holdersLength = holders.length, var argsIndex = -1,
argsIndex = -1, argsLength = args.length,
argsLength = nativeMax(args.length - holdersLength, 0), holdersLength = holders.length,
leftIndex = -1, leftIndex = -1,
leftLength = partials.length, leftLength = partials.length,
result = Array(leftLength + argsLength); rangeLength = nativeMax(argsLength - holdersLength, 0),
result = Array(leftLength + rangeLength),
isUncurried = !isCurried;
while (++leftIndex < leftLength) { while (++leftIndex < leftLength) {
result[leftIndex] = partials[leftIndex]; result[leftIndex] = partials[leftIndex];
} }
while (++argsIndex < holdersLength) { while (++argsIndex < holdersLength) {
result[holders[argsIndex]] = args[argsIndex]; if (isUncurried || argsIndex < argsLength) {
result[holders[argsIndex]] = args[argsIndex];
}
} }
while (argsLength--) { while (rangeLength--) {
result[leftIndex++] = args[argsIndex++]; result[leftIndex++] = args[argsIndex++];
} }
return result; return result;
@@ -3900,18 +3926,21 @@
* @param {Array|Object} args The provided arguments. * @param {Array|Object} args The provided arguments.
* @param {Array} partials The arguments to append to those provided. * @param {Array} partials The arguments to append to those provided.
* @param {Array} holders The `partials` placeholder indexes. * @param {Array} holders The `partials` placeholder indexes.
* @params {boolean} [isCurried] Specify composing for a curried function.
* @returns {Array} Returns the new array of composed arguments. * @returns {Array} Returns the new array of composed arguments.
*/ */
function composeArgsRight(args, partials, holders) { function composeArgsRight(args, partials, holders, isCurried) {
var holdersIndex = -1, var argsIndex = -1,
argsLength = args.length,
holdersIndex = -1,
holdersLength = holders.length, holdersLength = holders.length,
argsIndex = -1,
argsLength = nativeMax(args.length - holdersLength, 0),
rightIndex = -1, rightIndex = -1,
rightLength = partials.length, rightLength = partials.length,
result = Array(argsLength + rightLength); rangeLength = nativeMax(argsLength - holdersLength, 0),
result = Array(rangeLength + rightLength),
isUncurried = !isCurried;
while (++argsIndex < argsLength) { while (++argsIndex < rangeLength) {
result[argsIndex] = args[argsIndex]; result[argsIndex] = args[argsIndex];
} }
var offset = argsIndex; var offset = argsIndex;
@@ -3919,7 +3948,9 @@
result[offset + rightIndex] = partials[rightIndex]; result[offset + rightIndex] = partials[rightIndex];
} }
while (++holdersIndex < holdersLength) { while (++holdersIndex < holdersLength) {
result[offset + holders[holdersIndex]] = args[argsIndex++]; if (isUncurried || argsIndex < argsLength) {
result[offset + holders[holdersIndex]] = args[argsIndex++];
}
} }
return result; return result;
} }
@@ -4306,8 +4337,7 @@
var isAry = bitmask & ARY_FLAG, var isAry = bitmask & ARY_FLAG,
isBind = bitmask & BIND_FLAG, isBind = bitmask & BIND_FLAG,
isBindKey = bitmask & BIND_KEY_FLAG, isBindKey = bitmask & BIND_KEY_FLAG,
isCurry = bitmask & CURRY_FLAG, isCurried = bitmask & (CURRY_FLAG | CURRY_RIGHT_FLAG),
isCurryRight = bitmask & CURRY_RIGHT_FLAG,
isFlip = bitmask & FLIP_FLAG, isFlip = bitmask & FLIP_FLAG,
Ctor = isBindKey ? undefined : createCtorWrapper(func); Ctor = isBindKey ? undefined : createCtorWrapper(func);
@@ -4319,33 +4349,34 @@
while (index--) { while (index--) {
args[index] = arguments[index]; args[index] = arguments[index];
} }
if (isCurried) {
var placeholder = lodash.placeholder || wrapper.placeholder,
holdersCount = countHolders(args, placeholder);
}
if (partials) { if (partials) {
args = composeArgs(args, partials, holders); args = composeArgs(args, partials, holders, isCurried);
} }
if (partialsRight) { if (partialsRight) {
args = composeArgsRight(args, partialsRight, holdersRight); args = composeArgsRight(args, partialsRight, holdersRight, isCurried);
} }
if (isCurry || isCurryRight) { length -= holdersCount;
var placeholder = lodash.placeholder || wrapper.placeholder, if (isCurried && length < arity) {
argsHolders = replaceHolders(args, placeholder); var newHolders = replaceHolders(args, placeholder);
return createRecurryWrapper(
length -= argsHolders.length; func, bitmask, createHybridWrapper, placeholder, thisArg, args,
if (length < arity) { newHolders, argPos, ary, arity - length
return createRecurryWrapper( );
func, bitmask, createHybridWrapper, placeholder, thisArg, args,
argsHolders, argPos, ary, arity - length
);
}
} }
var thisBinding = isBind ? thisArg : this, var thisBinding = isBind ? thisArg : this,
fn = isBindKey ? thisBinding[func] : func; fn = isBindKey ? thisBinding[func] : func;
length = args.length;
if (argPos) { if (argPos) {
args = reorder(args, argPos); args = reorder(args, argPos);
} else if (isFlip && args.length > 1) { } else if (isFlip && length > 1) {
args.reverse(); args.reverse();
} }
if (isAry && ary < args.length) { if (isAry && ary < length) {
args.length = ary; args.length = ary;
} }
if (this && this !== root && this instanceof wrapper) { if (this && this !== root && this instanceof wrapper) {

View File

@@ -3612,6 +3612,16 @@
assert.deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3, 4]); assert.deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3, 4]);
}); });
QUnit.test('should persist placeholders', function(assert) {
assert.expect(1);
var curried = _.curry(fn),
ph = curried.placeholder,
actual = curried(ph, ph, ph, 'd')('a')(ph)('b')('c');
assert.deepEqual(actual, ['a', 'b', 'c', 'd']);
});
QUnit.test('should provide additional arguments after reaching the target arity', function(assert) { QUnit.test('should provide additional arguments after reaching the target arity', function(assert) {
assert.expect(3); assert.expect(3);
@@ -3745,6 +3755,16 @@
assert.deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), expected); assert.deepEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), expected);
}); });
QUnit.test('should persist placeholders', function(assert) {
assert.expect(1);
var curried = _.curryRight(fn),
ph = curried.placeholder,
actual = curried('a', ph, ph, ph)('b')(ph)('c')('d');
assert.deepEqual(actual, ['a', 'b', 'c', 'd']);
});
QUnit.test('should provide additional arguments after reaching the target arity', function(assert) { QUnit.test('should provide additional arguments after reaching the target arity', function(assert) {
assert.expect(3); assert.expect(3);