From 046470a8db09178fbe5071ef1f1e7507cca4241d Mon Sep 17 00:00:00 2001 From: Michael Morgan Date: Mon, 8 Feb 2016 17:09:13 -0600 Subject: [PATCH] Added `_.flattenDepth` for variable-depth flatten. --- fp/_mapping.js | 6 ++-- lodash.js | 88 ++++++++++++++++++++++++++++++++------------------ test/test.js | 24 ++++++++------ 3 files changed, 75 insertions(+), 43 deletions(-) diff --git a/fp/_mapping.js b/fp/_mapping.js index 42d7c993c..8975c1dd6 100644 --- a/fp/_mapping.js +++ b/fp/_mapping.js @@ -50,9 +50,9 @@ exports.aryMethod = { 'curryRightN', 'debounce', 'defaults', 'defaultsDeep', 'delay', 'difference', 'drop', 'dropRight', 'dropRightWhile', 'dropWhile', 'endsWith', 'eq', 'every', 'filter', 'find', 'find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', - 'findLastKey', 'flatMap', 'forEach', 'forEachRight', 'forIn', 'forInRight', - 'forOwn', 'forOwnRight', 'get', 'groupBy', 'gt', 'gte', 'has', 'hasIn', - 'includes', 'indexOf', 'intersection', 'invertBy', 'invoke', 'invokeMap', + 'findLastKey', 'flatMap', 'flattenDepth', 'forEach', 'forEachRight', 'forIn', + 'forInRight', 'forOwn', 'forOwnRight', 'get', 'groupBy', 'gt', 'gte', 'has', + 'hasIn', 'includes', 'indexOf', 'intersection', 'invertBy', 'invoke', 'invokeMap', 'isEqual', 'isMatch', 'join', 'keyBy', 'lastIndexOf', 'lt', 'lte', 'map', 'mapKeys', 'mapValues', 'matchesProperty', 'maxBy', 'merge', 'minBy', 'omit', 'omitBy', 'orderBy', 'overArgs', 'pad', 'padEnd', 'padStart', 'parseInt', diff --git a/lodash.js b/lodash.js index c824f8bbf..369ba5450 100644 --- a/lodash.js +++ b/lodash.js @@ -1399,23 +1399,23 @@ * `compact`, `concat`, `conforms`, `constant`, `countBy`, `create`, `curry`, * `debounce`, `defaults`, `defaultsDeep`, `defer`, `delay`, `difference`, * `differenceBy`, `differenceWith`, `drop`, `dropRight`, `dropRightWhile`, - * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flip`, `flow`, - * `flowRight`, `fromPairs`, `functions`, `functionsIn`, `groupBy`, `initial`, - * `intersection`, `intersectionBy`, `intersectionWith`, `invert`, `invertBy`, - * `invokeMap`, `iteratee`, `keyBy`, `keys`, `keysIn`, `map`, `mapKeys`, - * `mapValues`, `matches`, `matchesProperty`, `memoize`, `merge`, `mergeWith`, - * `method`, `methodOf`, `mixin`, `negate`, `nthArg`, `omit`, `omitBy`, `once`, - * `orderBy`, `over`, `overArgs`, `overEvery`, `overSome`, `partial`, - * `partialRight`, `partition`, `pick`, `pickBy`, `plant`, `property`, - * `propertyOf`, `pull`, `pullAll`, `pullAllBy`, `pullAt`, `push`, `range`, - * `rangeRight`, `rearg`, `reject`, `remove`, `rest`, `reverse`, `sampleSize`, - * `set`, `setWith`, `shuffle`, `slice`, `sort`, `sortBy`, `splice`, `spread`, - * `tail`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, `tap`, `throttle`, - * `thru`, `toArray`, `toPairs`, `toPairsIn`, `toPath`, `toPlainObject`, - * `transform`, `unary`, `union`, `unionBy`, `unionWith`, `uniq`, `uniqBy`, - * `uniqWith`, `unset`, `unshift`, `unzip`, `unzipWith`, `values`, `valuesIn`, - * `without`, `wrap`, `xor`, `xorBy`, `xorWith`, `zip`, `zipObject`, - * `zipObjectDeep`, and `zipWith` + * `dropWhile`, `fill`, `filter`, `flatten`, `flattenDeep`, `flattenDepth`, + * `flip`, `flow`, `flowRight`, `fromPairs`, `functions`, `functionsIn`, + * `groupBy`, `initial`, `intersection`, `intersectionBy`, `intersectionWith`, + * `invert`, `invertBy`, `invokeMap`, `iteratee`, `keyBy`, `keys`, `keysIn`, + * `map`, `mapKeys`, `mapValues`, `matches`, `matchesProperty`, `memoize`, + * `merge`, `mergeWith`, `method`, `methodOf`, `mixin`, `negate`, `nthArg`, + * `omit`, `omitBy`, `once`, `orderBy`, `over`, `overArgs`, `overEvery`, + * `overSome`, `partial`, `partialRight`, `partition`, `pick`, `pickBy`, `plant`, + * `property`, `propertyOf`, `pull`, `pullAll`, `pullAllBy`, `pullAt`, `push`, + * `range`, `rangeRight`, `rearg`, `reject`, `remove`, `rest`, `reverse`, + * `sampleSize`, `set`, `setWith`, `shuffle`, `slice`, `sort`, `sortBy`, + * `splice`, `spread`, `tail`, `take`, `takeRight`, `takeRightWhile`, + * `takeWhile`, `tap`, `throttle`, `thru`, `toArray`, `toPairs`, `toPairsIn`, + * `toPath`, `toPlainObject`, `transform`, `unary`, `union`, `unionBy`, + * `unionWith`, `uniq`, `uniqBy`, `uniqWith`, `unset`, `unshift`, `unzip`, + * `unzipWith`, `values`, `valuesIn`, `without`, `wrap`, `xor`, `xorBy`, + * `xorWith`, `zip`, `zipObject`, `zipObjectDeep`, and `zipWith` * * The wrapper methods that are **not** chainable by default are: * `add`, `attempt`, `camelCase`, `capitalize`, `ceil`, `clamp`, `clone`, @@ -2494,12 +2494,14 @@ * * @private * @param {Array} array The array to flatten. - * @param {boolean} [isDeep] Specify a deep flatten. + * @param {number} [depth=1] The maximum recursion depth. * @param {boolean} [isStrict] Restrict flattening to arrays-like objects. * @param {Array} [result=[]] The initial result value. * @returns {Array} Returns the new flattened array. */ - function baseFlatten(array, isDeep, isStrict, result) { + function baseFlatten(array, depth, isStrict, result) { + depth = depth === undefined ? 1 : depth; + result || (result = []); var index = -1, @@ -2509,9 +2511,9 @@ var value = array[index]; if (isArrayLikeObject(value) && (isStrict || isArray(value) || isArguments(value))) { - if (isDeep) { + if (depth > 1) { // Recursively flatten arrays (susceptible to call stack limits). - baseFlatten(value, isDeep, isStrict, result); + baseFlatten(value, depth - 1, isStrict, result); } else { arrayPush(result, value); } @@ -5475,7 +5477,7 @@ */ var difference = rest(function(array, values) { return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, false, true)) + ? baseDifference(array, baseFlatten(values, 1, true)) : []; }); @@ -5506,7 +5508,7 @@ iteratee = undefined; } return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, false, true), getIteratee(iteratee)) + ? baseDifference(array, baseFlatten(values, 1, true), getIteratee(iteratee)) : []; }); @@ -5535,7 +5537,7 @@ comparator = undefined; } return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, false, true), undefined, comparator) + ? baseDifference(array, baseFlatten(values, 1, true), undefined, comparator) : []; }); @@ -5814,7 +5816,7 @@ * @returns {Array} Returns the new flattened array. * @example * - * _.flatten([1, [2, 3, [4]]]); + * _.flatten([1, 2, [3, [4]]]); * // => [1, 2, 3, [4]] */ function flatten(array) { @@ -5832,12 +5834,35 @@ * @returns {Array} Returns the new flattened array. * @example * - * _.flattenDeep([1, [2, 3, [4]]]); - * // => [1, 2, 3, 4] + * _.flattenDeep([1, [2, [3, [[4]]], 5]]); + * // => [1, 2, 3, 4, 5] */ function flattenDeep(array) { var length = array ? array.length : 0; - return length ? baseFlatten(array, true) : []; + return length ? baseFlatten(array, INFINITY) : []; + } + + /** + * Recursively flatten `array` up to `depth` times. + * + * @static + * @memberOf _ + * @category Array + * @param {Array} array The array to flatten. + * @param {number} depth The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @example + * + * _.flattenDepth([1, [2, [3, [4]], 5]], 1); + * // => [1, 2, [3, [4]], 5] + * + * _.flattenDepth([1, [2, [3, [4]], 5]], 2); + * // => [1, 2, 3, [4], 5] + */ + function flattenDepth(array, depth) { + depth = toInteger(depth); + var length = array ? array.length : 0; + return length ? (depth >= 1 ? baseFlatten(array, depth) : copyArray(array)) : []; } /** @@ -6677,7 +6702,7 @@ * // => [2, 1, 4] */ var union = rest(function(arrays) { - return baseUniq(baseFlatten(arrays, false, true)); + return baseUniq(baseFlatten(arrays, 1, true)); }); /** @@ -6705,7 +6730,7 @@ if (isArrayLikeObject(iteratee)) { iteratee = undefined; } - return baseUniq(baseFlatten(arrays, false, true), getIteratee(iteratee)); + return baseUniq(baseFlatten(arrays, 1, true), getIteratee(iteratee)); }); /** @@ -6732,7 +6757,7 @@ if (isArrayLikeObject(comparator)) { comparator = undefined; } - return baseUniq(baseFlatten(arrays, false, true), undefined, comparator); + return baseUniq(baseFlatten(arrays, 1, true), undefined, comparator); }); /** @@ -14208,6 +14233,7 @@ lodash.flatMap = flatMap; lodash.flatten = flatten; lodash.flattenDeep = flattenDeep; + lodash.flattenDepth = flattenDepth; lodash.flip = flip; lodash.flow = flow; lodash.flowRight = flowRight; diff --git a/test/test.js b/test/test.js index 5d3cea8d9..b32291b4a 100644 --- a/test/test.js +++ b/test/test.js @@ -5620,31 +5620,37 @@ }); QUnit.test('should work with empty arrays', function(assert) { - assert.expect(2); + assert.expect(3); var array = [[], [[]], [[], [[[]]]]]; assert.deepEqual(_.flatten(array), [[], [], [[[]]]]); assert.deepEqual(_.flattenDeep(array), []); + assert.deepEqual(_.flattenDepth(array, 2), [[[]]]) }); QUnit.test('should support flattening of nested arrays', function(assert) { - assert.expect(2); + assert.expect(4); - var array = [1, [2, 3], 4, [[5]]]; + var array = [1, [[2, 3], 4, [[[5]]]]]; - assert.deepEqual(_.flatten(array), [1, 2, 3, 4, [5]]); + assert.deepEqual(_.flatten(array), [1, [2, 3], 4, [[[5]]]]); assert.deepEqual(_.flattenDeep(array), [1, 2, 3, 4, 5]); + assert.deepEqual(_.flattenDepth(array, 2), [1, 2, 3, 4, [[5]]]); + assert.deepEqual(_.flattenDepth(array, 3), [1, 2, 3, 4, [5]]); }); QUnit.test('should return an empty array for non array-like objects', function(assert) { - assert.expect(3); + assert.expect(5); + var nonArray = { 'a': 1 }; var expected = []; - assert.deepEqual(_.flatten({ 'a': 1 }), expected); - assert.deepEqual(_.flatten({ 'a': 1 }, true), expected); - assert.deepEqual(_.flattenDeep({ 'a': 1 }), expected); + assert.deepEqual(_.flatten(nonArray), expected); + assert.deepEqual(_.flatten(nonArray, true), expected); + assert.deepEqual(_.flattenDeep(nonArray), expected); + assert.deepEqual(_.flattenDepth(nonArray, 2), expected); + assert.deepEqual(_.flattenDepth(nonArray, 0), expected); }); QUnit.test('should return a wrapped value when chaining', function(assert) { @@ -24035,7 +24041,7 @@ var acceptFalsey = lodashStable.difference(allMethods, rejectFalsey); QUnit.test('should accept falsey arguments', function(assert) { - assert.expect(295); + assert.expect(296); var emptyArrays = lodashStable.map(falsey, alwaysEmptyArray);