mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-02 16:17:50 +00:00
Add _.differenceWith, _.intersectionWith, _.unionWith, _.uniqWith, and _.xorWith.
This commit is contained in:
320
lodash.js
320
lodash.js
@@ -723,6 +723,22 @@
|
||||
return -1;
|
||||
}
|
||||
|
||||
function baseIncludes(array, value) {
|
||||
return baseIndexOf(array, value, 0) > -1;
|
||||
}
|
||||
|
||||
function includesWith(array, value, comparator) {
|
||||
var index = -1,
|
||||
length = array ? array.length : 0;
|
||||
|
||||
while (++index < length) {
|
||||
if (comparator(value, array[index])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.pairs` and `_.pairsIn` which creates an array
|
||||
* of key-value pairs for `object` corresponding to the property names of `props`.
|
||||
@@ -821,6 +837,12 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
function baseUnary(func) {
|
||||
return function(value) {
|
||||
return func(value);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.values` and `_.valuesIn` which creates an
|
||||
* array of `object` property values corresponding to the property names
|
||||
@@ -1881,23 +1903,22 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is in `cache` mimicking the return signature of
|
||||
* `_.indexOf` by returning `0` if `value` is found, else `-1`.
|
||||
* Checks if `value` is in `cache`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} cache The set cache to search.
|
||||
* @param {*} value The value to search for.
|
||||
* @returns {number} Returns `0` if `value` is found, else `-1`.
|
||||
* @returns {number} Returns `true` if `value` is found, else `false`.
|
||||
*/
|
||||
function cacheIndexOf(cache, value) {
|
||||
function cacheHas(cache, value) {
|
||||
var map = cache.__data__;
|
||||
if (isKeyable(value)) {
|
||||
var data = map.__data__,
|
||||
hash = typeof value == 'string' ? data.string : data.hash;
|
||||
|
||||
return hash[value] === HASH_UNDEFINED ? 0 : -1;
|
||||
return hash[value] === HASH_UNDEFINED;
|
||||
}
|
||||
return map.has(value) ? 0 : -1;
|
||||
return map.has(value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2267,21 +2288,8 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.difference` without support for excluding
|
||||
* multiple arrays of values.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to inspect.
|
||||
* @param {Array} values The values to exclude.
|
||||
* @returns {Array} Returns the new array of filtered values.
|
||||
*/
|
||||
function baseDifference(array, values) {
|
||||
return baseDifferenceBy(array, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.differenceBy` without support for callback
|
||||
* shorthands.
|
||||
* The base implementation of methods like `_.difference` without support for
|
||||
* excluding multiple arrays or callback shorthands.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to inspect.
|
||||
@@ -2289,9 +2297,9 @@
|
||||
* @param {Function} [iteratee] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of filtered values.
|
||||
*/
|
||||
function baseDifferenceBy(array, values, iteratee) {
|
||||
function baseDifference(array, values, iteratee, comparator) {
|
||||
var index = -1,
|
||||
indexOf = baseIndexOf,
|
||||
includes = baseIncludes,
|
||||
isCommon = true,
|
||||
length = array.length,
|
||||
result = [],
|
||||
@@ -2301,10 +2309,14 @@
|
||||
return result;
|
||||
}
|
||||
if (iteratee) {
|
||||
values = arrayMap(values, function(value) { return iteratee(value); });
|
||||
values = arrayMap(values, baseUnary(iteratee));
|
||||
}
|
||||
if (values.length >= LARGE_ARRAY_SIZE) {
|
||||
indexOf = cacheIndexOf;
|
||||
if (comparator) {
|
||||
includes = includesWith;
|
||||
isCommon = false;
|
||||
}
|
||||
else if (values.length >= LARGE_ARRAY_SIZE) {
|
||||
includes = cacheHas;
|
||||
isCommon = false;
|
||||
values = new SetCache(values);
|
||||
}
|
||||
@@ -2322,7 +2334,7 @@
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
else if (indexOf(values, computed, 0) < 0) {
|
||||
else if (!includes(values, computed, comparator)) {
|
||||
result.push(value);
|
||||
}
|
||||
}
|
||||
@@ -2573,28 +2585,17 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.intersection` that accepts an array of arrays
|
||||
* to inspect.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} arrays The arrays to inspect.
|
||||
* @returns {Array} Returns the new array of shared values.
|
||||
*/
|
||||
function baseIntersection(arrays) {
|
||||
return baseIntersectionBy(arrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.intersectionBy` without support for callback
|
||||
* shorthands.
|
||||
* The base implementation of methods like `_.intersection`, without support
|
||||
* for callback shorthands, that accepts an array of arrays to inspect.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} arrays The arrays to inspect.
|
||||
* @param {Function} [iteratee] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of shared values.
|
||||
*/
|
||||
function baseIntersectionBy(arrays, iteratee) {
|
||||
var othLength = arrays.length,
|
||||
function baseIntersection(arrays, iteratee, comparator) {
|
||||
var includes = comparator ? includesWith : baseIncludes,
|
||||
othLength = arrays.length,
|
||||
othIndex = othLength,
|
||||
caches = Array(othLength),
|
||||
result = [];
|
||||
@@ -2602,9 +2603,11 @@
|
||||
while (othIndex--) {
|
||||
var array = arrays[othIndex];
|
||||
if (othIndex && iteratee) {
|
||||
array = arrayMap(array, function(value) { return iteratee(value); });
|
||||
array = arrayMap(array, baseUnary(iteratee));
|
||||
}
|
||||
caches[othIndex] = (iteratee || array.length >= 120) ? new SetCache(othIndex && array) : null;
|
||||
caches[othIndex] = !comparator && (iteratee || array.length >= 120)
|
||||
? new SetCache(othIndex && array)
|
||||
: undefined;
|
||||
}
|
||||
array = arrays[0];
|
||||
|
||||
@@ -2617,11 +2620,11 @@
|
||||
var value = array[index],
|
||||
computed = iteratee ? iteratee(value) : value;
|
||||
|
||||
if ((seen ? cacheIndexOf(seen, computed) : baseIndexOf(result, computed, 0)) < 0) {
|
||||
if (!(seen ? cacheHas(seen, computed) : includes(result, computed, comparator))) {
|
||||
var othIndex = othLength;
|
||||
while (--othIndex) {
|
||||
var cache = caches[othIndex];
|
||||
if ((cache ? cacheIndexOf(cache, computed) : baseIndexOf(arrays[othIndex], computed, 0)) < 0) {
|
||||
if (!(cache ? cacheHas(cache, computed) : includes(arrays[othIndex], computed, comparator))) {
|
||||
continue outer;
|
||||
}
|
||||
}
|
||||
@@ -3321,18 +3324,6 @@
|
||||
return isArray(value) ? value : stringToPath(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.uniq`.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to inspect.
|
||||
* @param {Function} [iteratee] The function invoked per iteration.
|
||||
* @returns {Array} Returns the new duplicate free array.
|
||||
*/
|
||||
function baseUniq(array) {
|
||||
return baseUniqBy(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.uniqBy` without support for callback shorthands.
|
||||
*
|
||||
@@ -3341,18 +3332,25 @@
|
||||
* @param {Function} [iteratee] The function invoked per element.
|
||||
* @returns {Array} Returns the new duplicate free array.
|
||||
*/
|
||||
function baseUniqBy(array, iteratee) {
|
||||
var index = -1,
|
||||
indexOf = baseIndexOf,
|
||||
function baseUniq(array, iteratee, comparator) {
|
||||
var seen,
|
||||
index = -1,
|
||||
includes = baseIncludes,
|
||||
length = array.length,
|
||||
isCommon = true,
|
||||
seen = length >= LARGE_ARRAY_SIZE ? new SetCache : null,
|
||||
result = [];
|
||||
result = [],
|
||||
seen = result;
|
||||
|
||||
if (seen) {
|
||||
indexOf = cacheIndexOf;
|
||||
if (comparator) {
|
||||
isCommon = false;
|
||||
} else {
|
||||
includes = includesWith;
|
||||
}
|
||||
else if (length >= LARGE_ARRAY_SIZE) {
|
||||
isCommon = false;
|
||||
includes = cacheHas;
|
||||
seen = new SetCache;
|
||||
}
|
||||
else {
|
||||
seen = iteratee ? [] : result;
|
||||
}
|
||||
outer:
|
||||
@@ -3372,7 +3370,7 @@
|
||||
}
|
||||
result.push(value);
|
||||
}
|
||||
else if (indexOf(seen, computed, 0) < 0) {
|
||||
else if (!includes(seen, computed, comparator)) {
|
||||
if (seen !== result) {
|
||||
seen.push(computed);
|
||||
}
|
||||
@@ -3441,34 +3439,27 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.xor` that accepts an array of arrays to inspect.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} arrays The arrays to inspect.
|
||||
* @returns {Array} Returns the new array of values.
|
||||
*/
|
||||
function baseXor(arrays) {
|
||||
return baseXorBy(arrays);
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.xorBy` without support for callback shorthands.
|
||||
* The base implementation of methods like `_.xor`, without support for
|
||||
* callback shorthands, that accepts an array of arrays to inspect.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} arrays The arrays to inspect.
|
||||
* @param {Function} [iteratee] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of values.
|
||||
*/
|
||||
function baseXorBy(arrays, iteratee) {
|
||||
function baseXor(arrays, iteratee, comparator) {
|
||||
var index = -1,
|
||||
length = arrays.length;
|
||||
|
||||
while (++index < length) {
|
||||
var result = result
|
||||
? arrayPush(baseDifferenceBy(result, arrays[index], iteratee), baseDifferenceBy(arrays[index], result, iteratee))
|
||||
? arrayPush(
|
||||
baseDifference(result, arrays[index], iteratee, comparator),
|
||||
baseDifference(arrays[index], result, iteratee, comparator)
|
||||
)
|
||||
: arrays[index];
|
||||
}
|
||||
return (result && result.length) ? baseUniqBy(result, iteratee) : [];
|
||||
return (result && result.length) ? baseUniq(result, iteratee, comparator) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -5101,7 +5092,36 @@
|
||||
iteratee = undefined;
|
||||
}
|
||||
return isArrayLikeObject(array)
|
||||
? baseDifferenceBy(array, baseFlatten(values, false, true), getIteratee(iteratee))
|
||||
? baseDifference(array, baseFlatten(values, false, true), getIteratee(iteratee))
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.difference` except that it accepts `comparator`
|
||||
* which is invoked to compare elements of `array` to `values`. The comparator
|
||||
* is invoked with two arguments: (arrVal, othVal).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Array
|
||||
* @param {Array} array The array to inspect.
|
||||
* @param {...Array} [values] The values to exclude.
|
||||
* @param {Function} [comparator] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of filtered values.
|
||||
* @example
|
||||
*
|
||||
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
|
||||
*
|
||||
* _.differenceWith(objects, [{ 'x': 1, 'y': 2 }], _.isEqual);
|
||||
* // => [{ 'x': 2, 'y': 1 }]
|
||||
*/
|
||||
var differenceWith = rest(function(array, values) {
|
||||
var comparator = last(values);
|
||||
if (isArrayLikeObject(comparator)) {
|
||||
comparator = undefined;
|
||||
}
|
||||
return isArrayLikeObject(array)
|
||||
? baseDifference(array, baseFlatten(values, false, true), undefined, comparator)
|
||||
: [];
|
||||
});
|
||||
|
||||
@@ -5533,7 +5553,40 @@
|
||||
mapped.pop();
|
||||
}
|
||||
return (mapped.length && mapped[0] === arrays[0])
|
||||
? baseIntersectionBy(mapped, getIteratee(iteratee))
|
||||
? baseIntersection(mapped, getIteratee(iteratee))
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.intersection` except that it accepts `comparator`
|
||||
* which is invoked to compare elements of `arrays`. The comparator is invoked
|
||||
* with two arguments: (arrVal, othVal).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Array
|
||||
* @param {...Array} [arrays] The arrays to inspect.
|
||||
* @param {Function} [comparator] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of shared values.
|
||||
* @example
|
||||
*
|
||||
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
|
||||
* var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
|
||||
*
|
||||
* _.intersectionWith(objects, others, _.isEqual);
|
||||
* // => [{ 'x': 1, 'y': 2 }]
|
||||
*/
|
||||
var intersectionWith = rest(function(arrays) {
|
||||
var comparator = last(arrays),
|
||||
mapped = arrayMap(arrays, toArrayLikeObject);
|
||||
|
||||
if (comparator === last(mapped)) {
|
||||
comparator = undefined;
|
||||
} else {
|
||||
mapped.pop();
|
||||
}
|
||||
return (mapped.length && mapped[0] === arrays[0])
|
||||
? baseIntersection(mapped, undefined, comparator)
|
||||
: [];
|
||||
});
|
||||
|
||||
@@ -5662,7 +5715,7 @@
|
||||
*
|
||||
* var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }];
|
||||
*
|
||||
* _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], function(o) { return o.x; });
|
||||
* _.pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x');
|
||||
* console.log(array);
|
||||
* // => [{ 'x': 2 }]
|
||||
*/
|
||||
@@ -6198,7 +6251,34 @@
|
||||
if (isArrayLikeObject(iteratee)) {
|
||||
iteratee = undefined;
|
||||
}
|
||||
return baseUniqBy(baseFlatten(arrays, false, true), getIteratee(iteratee));
|
||||
return baseUniq(baseFlatten(arrays, false, true), getIteratee(iteratee));
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.union` except that it accepts `comparator` which
|
||||
* is invoked to compare elements of `arrays`. The comparator is invoked
|
||||
* with two arguments: (arrVal, othVal).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Array
|
||||
* @param {...Array} [arrays] The arrays to inspect.
|
||||
* @param {Function} [comparator] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of combined values.
|
||||
* @example
|
||||
*
|
||||
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
|
||||
* var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
|
||||
*
|
||||
* _.unionWith(objects, others, _.isEqual);
|
||||
* // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
|
||||
*/
|
||||
var unionWith = rest(function(arrays) {
|
||||
var comparator = last(arrays);
|
||||
if (isArrayLikeObject(comparator)) {
|
||||
iteratee = undefined;
|
||||
}
|
||||
return baseUniq(baseFlatten(arrays, false, true), undefined, comparator);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -6245,7 +6325,31 @@
|
||||
*/
|
||||
function uniqBy(array, iteratee) {
|
||||
return (array && array.length)
|
||||
? baseUniqBy(array, getIteratee(iteratee))
|
||||
? baseUniq(array, getIteratee(iteratee))
|
||||
: [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is like `_.uniq` except that it accepts `comparator` which
|
||||
* is invoked to compare elements of `array`. The comparator is invoked with
|
||||
* two arguments: (arrVal, othVal).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Array
|
||||
* @param {Array} array The array to inspect.
|
||||
* @param {Function} [comparator] The function invoked per element.
|
||||
* @returns {Array} Returns the new duplicate free array.
|
||||
* @example
|
||||
*
|
||||
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }];
|
||||
*
|
||||
* _.uniqWith(objects, _.isEqual);
|
||||
* // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]
|
||||
*/
|
||||
function uniqWith(array, comparator) {
|
||||
return (array && array.length)
|
||||
? baseUniq(array, undefined, comparator)
|
||||
: [];
|
||||
}
|
||||
|
||||
@@ -6381,7 +6485,34 @@
|
||||
if (isArrayLikeObject(iteratee)) {
|
||||
iteratee = undefined;
|
||||
}
|
||||
return baseXorBy(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee));
|
||||
return baseXor(arrayFilter(arrays, isArrayLikeObject), getIteratee(iteratee));
|
||||
});
|
||||
|
||||
/**
|
||||
* This method is like `_.xor` except that it accepts `comparator` which is
|
||||
* invoked to compare elements of `arrays`. The comparator is invoked with
|
||||
* two arguments: (arrVal, othVal).
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @category Array
|
||||
* @param {...Array} [arrays] The arrays to inspect.
|
||||
* @param {Function} [comparator] The function invoked per element.
|
||||
* @returns {Array} Returns the new array of values.
|
||||
* @example
|
||||
*
|
||||
* var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }];
|
||||
* var others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }];
|
||||
*
|
||||
* _.xorWith(objects, others, _.isEqual);
|
||||
* // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }]
|
||||
*/
|
||||
var xorWith = rest(function(arrays) {
|
||||
var comparator = last(arrays);
|
||||
if (isArrayLikeObject(comparator)) {
|
||||
comparator = undefined;
|
||||
}
|
||||
return baseXor(arrayFilter(arrays, isArrayLikeObject), undefined, comparator);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -7062,7 +7193,7 @@
|
||||
* @param {*} target The value to search for.
|
||||
* @param {number} [fromIndex=0] The index to search from.
|
||||
* @param- {Object} [guard] Enables use as an iteratee for functions like `_.reduce`.
|
||||
* @returns {boolean} Returns `true` if a matching element is found, else `false`.
|
||||
* @returns {boolean} Returns `true` if `target` is found, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.includes([1, 2, 3], 1);
|
||||
@@ -8615,6 +8746,10 @@
|
||||
return partial(wrapper, value);
|
||||
}
|
||||
|
||||
function unary(func) {
|
||||
return ary(func, 1);
|
||||
}
|
||||
|
||||
/*------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
@@ -13120,6 +13255,7 @@
|
||||
lodash.delay = delay;
|
||||
lodash.difference = difference;
|
||||
lodash.differenceBy = differenceBy;
|
||||
lodash.differenceWith = differenceWith;
|
||||
lodash.disj = disj;
|
||||
lodash.drop = drop;
|
||||
lodash.dropRight = dropRight;
|
||||
@@ -13138,6 +13274,7 @@
|
||||
lodash.initial = initial;
|
||||
lodash.intersection = intersection;
|
||||
lodash.intersectionBy = intersectionBy;
|
||||
lodash.intersectionWith = intersectionWith;
|
||||
lodash.invert = invert;
|
||||
lodash.invoke = invoke;
|
||||
lodash.iteratee = iteratee;
|
||||
@@ -13207,8 +13344,10 @@
|
||||
lodash.transform = transform;
|
||||
lodash.union = union;
|
||||
lodash.unionBy = unionBy;
|
||||
lodash.unionWith = unionWith;
|
||||
lodash.uniq = uniq;
|
||||
lodash.uniqBy = uniqBy;
|
||||
lodash.uniqWith = uniqWith;
|
||||
lodash.unset = unset;
|
||||
lodash.unzip = unzip;
|
||||
lodash.unzipWith = unzipWith;
|
||||
@@ -13219,6 +13358,7 @@
|
||||
lodash.wrap = wrap;
|
||||
lodash.xor = xor;
|
||||
lodash.xorBy = xorBy;
|
||||
lodash.xorWith = xorWith;
|
||||
lodash.zip = zip;
|
||||
lodash.zipObject = zipObject;
|
||||
lodash.zipWith = zipWith;
|
||||
|
||||
@@ -22028,7 +22028,7 @@
|
||||
var acceptFalsey = lodashStable.difference(allMethods, rejectFalsey);
|
||||
|
||||
QUnit.test('should accept falsey arguments', function(assert) {
|
||||
assert.expect(267);
|
||||
assert.expect(272);
|
||||
|
||||
var emptyArrays = lodashStable.map(falsey, lodashStable.constant([]));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user