diff --git a/fp/_mapping.js b/fp/_mapping.js index a2fa78344..0512a38bb 100644 --- a/fp/_mapping.js +++ b/fp/_mapping.js @@ -69,16 +69,17 @@ exports.aryMethod = { 'get', 'groupBy', 'gt', 'gte', 'has', 'hasIn', 'includes', 'indexOf', 'intersection', 'invertBy', 'invoke', 'invokeMap', 'isEqual', 'isMatch', 'join', 'keyBy', 'lastIndexOf', 'lt', 'lte', 'map', 'mapKeys', 'mapValues', - 'matchesProperty', 'maxBy', 'meanBy', 'merge', 'minBy', 'multiply', 'omit', 'omitBy', - 'overArgs', 'pad', 'padEnd', 'padStart', 'parseInt', 'partial', 'partialRight', - 'partition', 'pick', 'pickBy', 'pull', 'pullAll', 'pullAt', 'random', 'range', - 'rangeRight', 'rearg', 'reject', 'remove', 'repeat', 'restFrom', 'result', - 'sampleSize', 'some', 'sortBy', 'sortedIndex', 'sortedIndexOf', 'sortedLastIndex', - 'sortedLastIndexOf', 'sortedUniqBy', 'split', 'spreadFrom', 'startsWith', - 'subtract', 'sumBy', 'take', 'takeRight', 'takeRightWhile', 'takeWhile', 'tap', - 'throttle', 'thru', 'times', 'trimChars', 'trimCharsEnd', 'trimCharsStart', - 'truncate', 'union', 'uniqBy', 'uniqWith', 'unset', 'unzipWith', 'without', - 'wrap', 'xor', 'zip', 'zipObject', 'zipObjectDeep' + 'matchesProperty', 'maxBy', 'meanBy', 'merge', 'minBy', 'multiply', 'nth', + 'omit', 'omitBy', 'overArgs', 'pad', 'padEnd', 'padStart', 'parseInt', + 'partial', 'partialRight', 'partition', 'pick', 'pickBy', 'pull', 'pullAll', + 'pullAt', 'random', 'range', 'rangeRight', 'rearg', 'reject', 'remove', + 'repeat', 'restFrom', 'result', 'sampleSize', 'some', 'sortBy', 'sortedIndex', + 'sortedIndexOf', 'sortedLastIndex', 'sortedLastIndexOf', 'sortedUniqBy', + 'split', 'spreadFrom', 'startsWith', 'subtract', 'sumBy', 'take', 'takeRight', + 'takeRightWhile', 'takeWhile', 'tap', 'throttle', 'thru', 'times', 'trimChars', + 'trimCharsEnd', 'trimCharsStart', 'truncate', 'union', 'uniqBy', 'uniqWith', + 'unset', 'unzipWith', 'without', 'wrap', 'xor', 'zip', 'zipObject', + 'zipObjectDeep' ], '3': [ 'assignInWith', 'assignWith', 'clamp', 'differenceBy', 'differenceWith', diff --git a/lodash.js b/lodash.js index f6633fbd5..cbf6c706f 100644 --- a/lodash.js +++ b/lodash.js @@ -1552,7 +1552,7 @@ * `isSet`, `isString`, `isUndefined`, `isTypedArray`, `isWeakMap`, `isWeakSet`, * `join`, `kebabCase`, `last`, `lastIndexOf`, `lowerCase`, `lowerFirst`, * `lt`, `lte`, `max`, `maxBy`, `mean`, `meanBy`, `min`, `minBy`, `multiply`, - * `noConflict`, `noop`, `now`, `pad`, `padEnd`, `padStart`, `parseInt`, + * `noConflict`, `noop`, `now`, `nth`, `pad`, `padEnd`, `padStart`, `parseInt`, * `pop`, `random`, `reduce`, `reduceRight`, `repeat`, `result`, `round`, * `runInContext`, `sample`, `shift`, `size`, `snakeCase`, `some`, `sortedIndex`, * `sortedIndexBy`, `sortedLastIndex`, `sortedLastIndexBy`, `startCase`, @@ -3293,6 +3293,23 @@ assignMergeValue(object, key, newValue); } + /** + * The base implementation of `_.nth` which doesn't coerce `n` to an integer. + * + * @private + * @param {Array} array The array to inspect. + * @param {number} n The index of the element to return. + * @returns {*} Returns the nth element. + */ + function baseNth(array, n) { + var length = array.length; + if (!length) { + return undefined; + } + n += n < 0 ? length : 0; + return isIndex(n, length) ? array[n] : undefined; + } + /** * The base implementation of `_.orderBy` without param guards. * @@ -6654,6 +6671,31 @@ return -1; } + /** + * Gets the nth element of `array`. If `n` is negative, the nth element + * from the end is returned. + * + * @static + * @memberOf _ + * @since 4.11.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {number} [n=0] The index of the element to return.. + * @returns {*} Returns the nth element. + * @example + * + * var array = ['a', 'b', 'c', 'd']; + * + * _.nth(array, 1); + * // => 'b' + * + * _.nth(array, -2); + * // => 'c'; + */ + function nth(array, n) { + return (array && array.length) ? baseNth(array, toInteger(n)) : undefined; + } + /** * Removes all given values from `array` using * [`SameValueZero`](http://ecma-international.org/ecma-262/6.0/#sec-samevaluezero) @@ -14715,7 +14757,8 @@ } /** - * Creates a function that returns its nth argument. + * Creates a function that returns its nth argument. If `n` is negative, + * the nth argument from the end is returned. * * @static * @memberOf _ @@ -14726,15 +14769,18 @@ * @example * * var func = _.nthArg(1); - * - * func('a', 'b', 'c'); + * func('a', 'b', 'c', 'd'); * // => 'b' + * + * var func = _.nthArg(-2); + * func('a', 'b', 'c', 'd'); + * // => 'c' */ function nthArg(n) { n = toInteger(n); - return function() { - return arguments[n]; - }; + return rest(function(args) { + return baseNth(args, n); + }); } /** @@ -15642,6 +15688,7 @@ lodash.min = min; lodash.minBy = minBy; lodash.multiply = multiply; + lodash.nth = nth; lodash.noConflict = noConflict; lodash.noop = noop; lodash.now = now; diff --git a/test/test.js b/test/test.js index a3f58b219..c07148bab 100644 --- a/test/test.js +++ b/test/test.js @@ -15786,20 +15786,81 @@ /*--------------------------------------------------------------------------*/ - QUnit.module('lodash.nthArg'); + QUnit.module('lodash.nth'); (function() { - QUnit.test('should create a function that returns its nth argument', function(assert) { + var array = ['a', 'b', 'c', 'd']; + + QUnit.test('should get the nth element of `array`', function(assert) { assert.expect(1); - var expected = ['a', 'b', 'c']; + var actual = lodashStable.map(array, function(value, index) { + return _.nth(array, index); + }); - var actual = lodashStable.times(expected.length, function(n) { - var func = _.nthArg(n); - return func.apply(undefined, expected); + assert.deepEqual(actual, array); + }); + + QUnit.test('should work with a negative `n`', function(assert) { + assert.expect(1); + + var actual = lodashStable.map(lodashStable.range(1, array.length + 1), function(n) { + return _.nth(array, -n); + }); + + assert.deepEqual(actual, ['d', 'c', 'b', 'a']); + }); + + QUnit.test('should coerce `n` to an integer', function(assert) { + assert.expect(2); + + var values = falsey, + expected = lodashStable.map(values, alwaysA); + + var actual = lodashStable.map(values, function(n) { + return n ? _.nth(array, n) : _.nth(array); }); assert.deepEqual(actual, expected); + + values = ['1', 1.6]; + expected = lodashStable.map(values, alwaysB); + + actual = lodashStable.map(values, function(n) { + return _.nth(array, n); + }); + + assert.deepEqual(actual, expected); + }); + }()); + + /*--------------------------------------------------------------------------*/ + + QUnit.module('lodash.nthArg'); + + (function() { + var args = ['a', 'b', 'c', 'd']; + + QUnit.test('should create a function that returns its nth argument', function(assert) { + assert.expect(1); + + var actual = lodashStable.map(args, function(value, index) { + var func = _.nthArg(index); + return func.apply(undefined, args); + }); + + assert.deepEqual(actual, args); + }); + + QUnit.test('should work with a negative `n`', function(assert) { + assert.expect(1); + + var actual = lodashStable.map(lodashStable.range(1, args.length + 1), function(n) { + var func = _.nthArg(-n); + return func.apply(undefined, args); + }); + + assert.deepEqual(actual, ['d', 'c', 'b', 'a']); }); QUnit.test('should coerce `n` to an integer', function(assert) { @@ -15810,7 +15871,7 @@ var actual = lodashStable.map(values, function(n) { var func = n ? _.nthArg(n) : _.nthArg(); - return func('a', 'b', 'c'); + return func.apply(undefined, args); }); assert.deepEqual(actual, expected); @@ -15820,7 +15881,7 @@ actual = lodashStable.map(values, function(n) { var func = _.nthArg(n); - return func('a', 'b', 'c'); + return func.apply(undefined, args); }); assert.deepEqual(actual, expected); @@ -17799,7 +17860,7 @@ assert.deepEqual(func(1, 5, 20), [1]); }); - QUnit.test('`_.' + methodName + '` should work with a negative `step` argument', function(assert) { + QUnit.test('`_.' + methodName + '` should work with a negative `step`', function(assert) { assert.expect(2); assert.deepEqual(func(0, -4, -1), resolve([0, -1, -2, -3])); @@ -25520,7 +25581,7 @@ var acceptFalsey = lodashStable.difference(allMethods, rejectFalsey); QUnit.test('should accept falsey arguments', function(assert) { - assert.expect(307); + assert.expect(308); var emptyArrays = lodashStable.map(falsey, alwaysEmptyArray);