From 28e3ab73fadc66c4a15c176c23a7867e3efa3b04 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 30 Jul 2013 23:46:43 -0700 Subject: [PATCH] Add `_.remove`, `_.pull`, and fix typos in build. [closes #281] Former-commit-id: e771f3934c868d0d7093f1690d9c7d57fa75ae89 --- build.js | 93 +++++++++++++++++++------------------ build/pre-compile.js | 2 + lodash.js | 106 +++++++++++++++++++++++++++++++++++++++++-- test/test-build.js | 10 ++-- 4 files changed, 159 insertions(+), 52 deletions(-) diff --git a/build.js b/build.js index 64fa86685..40ea0c77c 100644 --- a/build.js +++ b/build.js @@ -184,11 +184,13 @@ 'partialRight': ['createBound'], 'pick': ['baseFlatten', 'createCallback', 'forIn', 'isObject'], 'pluck': ['map'], + 'pull': [], 'random': [], 'range': [], 'reduce': ['baseEach', 'createCallback', 'isArray'], 'reduceRight': ['createCallback', 'forEachRight'], 'reject': ['createCallback', 'filter'], + 'remove': ['baseEach', 'createCallback', 'isArray'], 'rest': ['createCallback', 'slice'], 'result': ['isFunction'], 'runInContext': ['defaults', 'pick'], @@ -322,6 +324,7 @@ 'intersection', 'last', 'lastIndexOf', + 'pull', 'range', 'rest', 'sortedIndex', @@ -357,6 +360,7 @@ 'reduce', 'reduceRight', 'reject', + 'remove', 'shuffle', 'size', 'some', @@ -553,6 +557,8 @@ 'merge', 'parseInt', 'partialRight', + 'pull', + 'remove', 'runInContext', 'transform' ]; @@ -2677,7 +2683,6 @@ }); if (warnings.length) { - funcDepMap = funcDepMapBackup; console.log([''].concat( warnings, 'For more information type: lodash --help' @@ -2693,65 +2698,65 @@ // update dependencies if (isLegacy) { - funcDepMap.defer = _.without(funcDepMap.defer, 'bind'); + _.pull(funcDepMap.defer, 'bind'); funcDepMap.isPlainObject = funcDepMap.shimIsPlainObject.slice(); funcDepMap.keys = funcDepMap.shimKeys.slice(); _.forOwn(varDepMap, function(deps, funcName) { if (funcName != 'createBound') { - varDepMap[funcName] = _.without(deps, 'reNative'); + _.pull(deps, 'reNative'); } }); } if (isMobile) { _.each(['assign', 'defaults'], function(funcName) { - funcDepMap[funcName] = _.without(funcDepMap[funcName], 'keys'); + _.pull(funcDepMap[funcName], 'keys'); }); } else if (isModern) { - funcDepMap.setBindData = _.without(funcDepMap.setBindData, 'noop'); + _.pull(funcDepMap.setBindData, 'noop'); _.forOwn(funcDepMap, function(deps, funcName) { if (funcName != 'baseFlatten' && _.contains(deps, 'isArguments')) { - funcDepMap[funcName] = _.without(deps, 'isArguments'); + _.pull(deps, 'isArguments'); } }); } if (isLegacy || isMobile || isUnderscore) { _.each(['createBound', 'createCallback'], function(funcName) { - funcDepMap[funcName] = _.without(funcDepMap[funcName], 'setBindData'); + _.pull(funcDepMap[funcName], 'setBindData'); }); } if (_.contains(plusFuncs, 'chain') == !isUnderscore) { - funcDepMap.mixin = _.without(funcDepMap.mixin, 'isFunction'); + _.pull(funcDepMap.mixin, 'isFunction'); } if (isUnderscore) { if (!isLodash('baseClone') && !isLodash('clone') && !isLodash('cloneDeep')) { - (funcDepMap.clone = _.without(funcDepMap.clone, 'baseClone')).push('assign', 'isArray', 'isObject'); + _.pull(funcDepMap.clone, 'baseClone').push('assign', 'isArray', 'isObject'); } if (!isLodash('contains')) { - funcDepMap.contains = _.without(funcDepMap.contains, 'isString'); + _.pull(funcDepMap.contains, 'isString'); } if (!isLodash('flatten')) { - funcDepMap.flatten = _.without(funcDepMap.flatten, 'map'); + _.pull(funcDepMap.flatten, 'map'); } if (!isLodash('isEmpty')) { funcDepMap.isEmpty = ['isArray', 'isString']; } if (!isLodash('baseIsEqual') && !isLodash('isEqual')) { - funcDepMap.baseIsEqual = _.without(funcDepMap.baseIsEqual, 'forIn', 'isArguments'); + _.pull(funcDepMap.baseIsEqual, 'forIn', 'isArguments'); } if (!isLodash('pick')){ - funcDepMap.pick = _.without(funcDepMap.pick, 'forIn', 'isObject'); + _.pull(funcDepMap.pick, 'forIn', 'isObject'); } if (!isLodash('template')) { - funcDepMap.template = _.without(funcDepMap.template, 'keys', 'values'); + _.pull(funcDepMap.template, 'keys', 'values'); } if (!isLodash('toArray')) { funcDepMap.toArray.push('isArray', 'map'); } if (!isLodash('where')) { - funcDepMap.createCallback = _.without(funcDepMap.createCallback, 'baseIsEqual'); + _.pull(funcDepMap.createCallback, 'baseIsEqual'); funcDepMap.where.push('find', 'isEmpty'); } if (!isLodash('forOwn')) { @@ -2767,7 +2772,7 @@ _.each(['baseUniq', 'difference', 'intersection'], function(funcName) { if (!isLodash(funcName)) { - (funcDepMap[funcName] = _.without(funcDepMap[funcName], 'cacheIndexOf', 'createCache')).push('getIndexOf'); + _.pull(funcDepMap[funcName], 'cacheIndexOf', 'createCache').push('getIndexOf'); } }); @@ -2789,7 +2794,7 @@ } } if (!isLodash(funcName)) { - funcDepMap[funcName] = _.without(funcDepMap[funcName], 'createCallback'); + _.pull(funcDepMap[funcName], 'createCallback'); } }); @@ -2799,10 +2804,10 @@ })) { deps = funcDepMap[funcName]; if (_.contains(deps, 'charAtCallback')) { - deps = funcDepMap[funcName] = _.without(deps, 'charAtCallback', 'isArray', 'isString'); + _.pull(deps, 'charAtCallback', 'isArray', 'isString'); } if (_.contains(deps, 'slice')) { - funcDepMap[funcName] = _.without(deps, 'slice'); + _.pull(deps, 'slice'); } } }); @@ -2810,8 +2815,9 @@ if (isModern || isUnderscore) { _.each(['assign', 'baseEach', 'defaults', 'forIn', 'forOwn', 'shimKeys'], function(funcName) { if (!(isUnderscore && isLodash(funcName))) { - (varDepMap[funcName] = _.without(varDepMap[funcName], 'defaultsIteratorOptions', 'eachIteratorOptions', 'forOwnIteratorOptions')).push('objectTypes'); - var deps = funcDepMap[funcName] = _.without(funcDepMap[funcName], 'createIterator'); + var deps = _.pull(funcDepMap[funcName], 'createIterator'); + _.pull(varDepMap[funcName] || (varDepMap[funcName] = []), 'defaultsIteratorOptions', 'eachIteratorOptions', 'forOwnIteratorOptions').push('objectTypes'); + if (funcName != 'shimKeys') { deps.push('createCallback'); } @@ -2825,16 +2831,16 @@ if (funcName != 'bind' && !(isMobile && funcName == 'keys') && !(isUnderscore && isLodash(funcName))) { - propDepMap[funcName] = _.without(deps, 'support'); + _.pull(deps, 'support'); } }); _.forOwn(funcDepMap, function(deps, funcName) { if (_.contains(deps, 'isNode')) { - deps = funcDepMap[funcName] = _.without(deps, 'isNode'); + _.pull(deps, 'isNode'); } if (_.contains(deps, 'toString') && (funcName != 'contains' && funcName != 'parseInt')) { - funcDepMap[funcName] = _.without(deps, 'isString'); + _.pull(deps, 'isString'); } }); @@ -2845,26 +2851,26 @@ })) { deps = funcDepMap[funcName]; if (_.contains(deps, 'releaseArray')) { - deps = funcDepMap[funcName] = _.without(deps, 'getArray', 'releaseArray'); + _.pull(deps, 'getArray', 'releaseArray'); } if (_.contains(deps, 'releaseObject')) { - funcDepMap[funcName] = _.without(deps, 'getObject', 'releaseObject'); + _.pull(deps, 'getObject', 'releaseObject'); } } }); } if (!isMobile) { _.each(['baseClone', 'transform', 'value'], function(funcName) { - (funcDepMap[funcName] = _.without(funcDepMap[funcName], 'baseEach')).push('forEach'); + _.pull(funcDepMap[funcName], 'baseEach').push('forEach'); }); - _.each(['contains', 'every', 'filter', 'find', 'forEach', 'map', 'max', 'min', 'reduce', 'some'], function(funcName) { - (funcDepMap[funcName] = _.without(funcDepMap[funcName], 'baseEach')).push('forOwn'); + _.each(['contains', 'every', 'filter', 'find', 'forEach', 'map', 'max', 'min', 'reduce', 'remove', 'some'], function(funcName) { + _.pull(funcDepMap[funcName], 'baseEach').push('forOwn'); }); - _.each(['every', 'find', 'filter', 'forEach', 'forIn', 'forOwn', 'map', 'reduce', 'shimKeys'], function(funcName) { + _.each(['every', 'find', 'filter', 'forEach', 'forIn', 'forOwn', 'map', 'reduce', 'remove', 'shimKeys'], function(funcName) { if (!(isUnderscore && isLodash(funcName))) { - funcDepMap[funcName] = _.without(funcDepMap[funcName], 'isArguments', 'isArray'); + _.pull(funcDepMap[funcName], 'isArguments', 'isArray'); } }); @@ -2879,10 +2885,10 @@ funcDepMap.createIterator.push('createCallback'); _.forOwn(funcDepMap, function(deps, funcName) { if (_.contains(deps, 'getIndexOf')) { - (deps = funcDepMap[funcName] = _.without(deps, 'getIndexOf')).push( 'baseIndexOf'); + _.pull(deps, 'getIndexOf').push( 'baseIndexOf'); } if (_.contains(deps, 'lodash') || _.contains(deps, 'lodashWrapper')) { - funcDepMap[funcName] = _.without(deps, 'lodash', 'lodashWrapper'); + _.pull(deps, 'lodash', 'lodashWrapper'); } }); } @@ -2906,7 +2912,7 @@ if (result == 'none') { result = []; } else { - result = _.without(result, 'none'); + _.pull(result, 'none'); } // add and subtract function names if (plusFuncs.length) { @@ -3117,7 +3123,7 @@ ].join('\n')); // replace `isArray(collection)` checks in "Collections" functions with simpler type checks - _.each(['every', 'filter', 'find', 'max', 'min', 'reduce', 'some'], function(funcName) { + _.each(['every', 'filter', 'find', 'max', 'min', 'reduce', 'remove', 'some'], function(funcName) { source = source.replace(matchFunction(source, funcName), function(match) { if (funcName == 'reduce') { match = match.replace(/^( *)var noaccum\b/m, '$1if (!collection) return accumulator;\n$&'); @@ -4245,20 +4251,19 @@ /*------------------------------------------------------------------------*/ // resolve `outputPath` and create directories if needed - outputPath = (function() { - var result = outputPath || options.reduce(function(result, value, index) { + if (!outputPath) { + outputPath = options.reduce(function(result, value, index) { if (/^(?:-o|--output)$/.test(value)) { result = options[index + 1]; - return path.join(fs.realpathSync(path.dirname(result)), path.basename(result)); + var dirname = path.dirname(result); + fs.mkdirpSync(dirname); + result = path.join(fs.realpathSync(dirname), path.basename(result)); } return result; }, ''); - - if (result) { - fs.mkdirpSync(path.dirname(result)); - } - return result; - }()); + } else { + fs.mkdirpSync(path.dirname(outputPath)); + } // flag to track if `outputPath` has been used by `callback` var outputUsed = false; diff --git a/build/pre-compile.js b/build/pre-compile.js index d81b20f72..dc2f76ebf 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -212,11 +212,13 @@ 'partialRight', 'pick', 'pluck', + 'pull', 'random', 'range', 'reduce', 'reduceRight', 'reject', + 'remove', 'rest', 'result', 'runInContext', diff --git a/lodash.js b/lodash.js index a9334fb87..87b66c3ea 100644 --- a/lodash.js +++ b/lodash.js @@ -497,6 +497,7 @@ propertyIsEnumerable = objectProto.propertyIsEnumerable, setImmediate = context.setImmediate, setTimeout = context.setTimeout, + splice = arrayRef.splice, toString = objectProto.toString, unshift = arrayRef.unshift; @@ -1667,7 +1668,7 @@ * @private * @type Function * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @returns {Array} Returns an array of property names. */ var shimKeys = createIterator({ 'args': 'object', @@ -1683,7 +1684,7 @@ * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names. + * @returns {Array} Returns an array of property names. * @example * * _.keys({ 'one': 1, 'two': 2, 'three': 3 }); @@ -2137,7 +2138,7 @@ * @alias methods * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property names that have function values. + * @returns {Array} Returns an array of property names that have function values. * @example * * _.functions(_); @@ -2834,7 +2835,7 @@ * @memberOf _ * @category Objects * @param {Object} object The object to inspect. - * @returns {Array} Returns a new array of property values. + * @returns {Array} Returns an array of property values. * @example * * _.values({ 'one': 1, 'two': 2, 'three': 3 }); @@ -3740,6 +3741,64 @@ }); } + /** + * Removes all elements from the `collection` that thw `callback` returns truthy + * for and returns an array of removed elements. The `callback` is bound to + * `thisArg` and invoked with three arguments; (value, index|key, collection). + * + * If a property name is passed for `callback`, the created "_.pluck" style + * callback will return the property value of the given element. + * + * If an object is passed for `callback`, the created "_.where" style callback + * will return `true` for elements that have the properties of the given object, + * else `false`. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to modify. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array} Returns a new array of removed elements. + * @example + * + * var array = [1, 2, 3, 4, 5, 6]; + * var evens = _.remove(array, function(num) { return num % 2 == 0; }); + * + * console.log(array); + * // => [1, 3, 5] + * + * console.log(evens); + * // => [2, 4, 6] + */ + function remove(collection, callback, thisArg) { + var result = []; + callback = lodash.createCallback(callback, thisArg, 3); + + if (isArray(collection)) { + var index = -1, + length = collection.length; + + while (++index < length) { + var value = collection[index]; + if (callback(value, index, collection)) { + result.push(value); + splice.call(collection, index, 1); + } + } + } else { + baseEach(collection, function(value, key, collection) { + if (callback(value, key, collection)) { + result.push(value); + delete collection[key]; + } + }); + } + return result; + } + /** * Creates an array of shuffled `array` values, using a version of the * Fisher-Yates shuffle. See http://en.wikipedia.org/wiki/Fisher-Yates_shuffle. @@ -4515,6 +4574,41 @@ return -1; } + /** + * Removes all passed values from the given array using strict equality for + * comparisons, i.e. `===`. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to modify. + * @param {Mixed} [value1, value2, ...] The values to remove. + * @returns {Array} Returns `array`. + * @example + * + * var array = [1, 2, 3, 1, 2, 3]; + * _.pull(array, 2, 3); + * console.log(array); + * // => [1, 1] + */ + function pull(array) { + var args = arguments, + argsIndex = 0, + argsLength = args.length, + length = array ? array.length : 0; + + while (++argsIndex < argsLength) { + var index = -1, + value = args[argsIndex]; + while (++index < length) { + if (array[index] === value) { + splice.call(array, index, 1); + } + } + } + return array; + } + /** * Creates an array of numbers (positive and/or negative) progressing from * `start` up to but not including `end`. If `start` is less than `stop` a @@ -5902,7 +5996,7 @@ * @param {Number} n The number of times to execute the callback. * @param {Function} callback The function called per iteration. * @param {Mixed} [thisArg] The `this` binding of `callback`. - * @returns {Array} Returns a new array of the results of each `callback` execution. + * @returns {Array} Returns an array of the results of each `callback` execution. * @example * * var diceRolls = _.times(3, _.partial(_.random, 1, 6)); @@ -6122,8 +6216,10 @@ lodash.partialRight = partialRight; lodash.pick = pick; lodash.pluck = pluck; + lodash.pull = pull; lodash.range = range; lodash.reject = reject; + lodash.remove = remove; lodash.rest = rest; lodash.shuffle = shuffle; lodash.sortBy = sortBy; diff --git a/test/test-build.js b/test/test-build.js index 892c56806..e10ccc40d 100644 --- a/test/test-build.js +++ b/test/test-build.js @@ -105,6 +105,7 @@ 'intersection', 'last', 'lastIndexOf', + 'pull', 'range', 'rest', 'sortedIndex', @@ -140,6 +141,7 @@ 'reduce', 'reduceRight', 'reject', + 'remove', 'shuffle', 'size', 'some', @@ -298,6 +300,8 @@ 'merge', 'parseInt', 'partialRight', + 'pull', + 'remove', 'runInContext', 'transform' ]; @@ -1740,7 +1744,7 @@ ); if (otherNames.length) { - funcNames = _.without(funcNames, category); + _.pull(funcNames, category); push.apply(funcNames, otherNames); } }); @@ -1749,10 +1753,10 @@ funcNames = _.uniq(_.intersection(expandFuncNames(funcNames), allFuncs)); if (!exposeAssign) { - funcNames = _.without(funcNames, 'assign'); + _.pull(funcNames, 'assign'); } if (!exposeZipObject) { - funcNames = _.without(funcNames, 'zipObject'); + _.pull(funcNames, 'zipObject'); } var lodash = context._ || {}; funcNames.forEach(function(funcName) {