mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-05 09:27:49 +00:00
Make toPath more robust and add support for deep properties to _.has and _.matchesProperty.
This commit is contained in:
182
lodash.src.js
182
lodash.src.js
@@ -87,34 +87,9 @@
|
|||||||
reEvaluate = /<%([\s\S]+?)%>/g,
|
reEvaluate = /<%([\s\S]+?)%>/g,
|
||||||
reInterpolate = /<%=([\s\S]+?)%>/g;
|
reInterpolate = /<%=([\s\S]+?)%>/g;
|
||||||
|
|
||||||
/**
|
/** Used to match property names within property paths. */
|
||||||
* Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
|
var reIsDeepProp = /\.|\[(?:\d+(?:\.\d+)?|(["'])(?:(?!\1)[^\n\\]|\\.)*?)\1\]/,
|
||||||
*/
|
rePropName = /([^.[\]]+)|\[(?:(\d+(?:\.\d+)?)|(["'])((?:(?!\3)[^\n\\]|\\.)*?)\3)\]/g;
|
||||||
var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components).
|
|
||||||
*/
|
|
||||||
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
|
|
||||||
|
|
||||||
/** Used to match `RegExp` flags from their coerced string values. */
|
|
||||||
var reFlags = /\w*$/;
|
|
||||||
|
|
||||||
/** Used to detect hexadecimal string values. */
|
|
||||||
var reHexPrefix = /^0[xX]/;
|
|
||||||
|
|
||||||
/** Used to detect host constructors (Safari > 5). */
|
|
||||||
var reHostCtor = /^\[object .+?Constructor\]$/;
|
|
||||||
|
|
||||||
/** Used to match latin-1 supplementary letters (excluding mathematical operators). */
|
|
||||||
var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
|
|
||||||
|
|
||||||
/** Used to ensure capturing order of template delimiters. */
|
|
||||||
var reNoMatch = /($^)/;
|
|
||||||
|
|
||||||
var reProp = /(?:[^.\\]|\\.)+/g,
|
|
||||||
reDeepProp = /[[.]/,
|
|
||||||
reBracketProp = /\[((?:[^[\\]|\\.)*?)\]/g;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
|
* Used to match `RegExp` [special characters](http://www.regular-expressions.info/characters.html#special).
|
||||||
@@ -124,6 +99,27 @@
|
|||||||
var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
|
var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
|
||||||
reHasRegExpChars = RegExp(reRegExpChars.source);
|
reHasRegExpChars = RegExp(reRegExpChars.source);
|
||||||
|
|
||||||
|
/** Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). */
|
||||||
|
var reComboMark = /[\u0300-\u036f\ufe20-\ufe23]/g;
|
||||||
|
|
||||||
|
/** Used to match [ES template delimiters](https://people.mozilla.org/~jorendorff/es6-draft.html#sec-template-literal-lexical-components). */
|
||||||
|
var reEsTemplate = /\$\{([^\\}]*(?:\\.[^\\}]*)*)\}/g;
|
||||||
|
|
||||||
|
/** Used to match `RegExp` flags from their coerced string values. */
|
||||||
|
var reFlags = /\w*$/;
|
||||||
|
|
||||||
|
/** Used to detect hexadecimal string values. */
|
||||||
|
var reHasHexPrefix = /^0[xX]/;
|
||||||
|
|
||||||
|
/** Used to detect host constructors (Safari > 5). */
|
||||||
|
var reIsHostCtor = /^\[object .+?Constructor\]$/;
|
||||||
|
|
||||||
|
/** Used to match latin-1 supplementary letters (excluding mathematical operators). */
|
||||||
|
var reLatin1 = /[\xc0-\xd6\xd8-\xde\xdf-\xf6\xf8-\xff]/g;
|
||||||
|
|
||||||
|
/** Used to ensure capturing order of template delimiters. */
|
||||||
|
var reNoMatch = /($^)/;
|
||||||
|
|
||||||
/** Used to match unescaped characters in compiled string literals. */
|
/** Used to match unescaped characters in compiled string literals. */
|
||||||
var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
|
var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
|
||||||
|
|
||||||
@@ -584,10 +580,6 @@
|
|||||||
(charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279)));
|
(charCode >= 8192 && (charCode <= 8202 || charCode == 8232 || charCode == 8233 || charCode == 8239 || charCode == 8287 || charCode == 12288 || charCode == 65279)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceBracket(match, key, index) {
|
|
||||||
return (index ? '.': '') + key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces all `placeholder` elements in `array` with an internal placeholder
|
* Replaces all `placeholder` elements in `array` with an internal placeholder
|
||||||
* and returns an array of their indexes.
|
* and returns an array of their indexes.
|
||||||
@@ -769,7 +761,7 @@
|
|||||||
var oldDash = context._;
|
var oldDash = context._;
|
||||||
|
|
||||||
/** Used to detect if a method is native. */
|
/** Used to detect if a method is native. */
|
||||||
var reNative = RegExp('^' +
|
var reIsNative = RegExp('^' +
|
||||||
escapeRegExp(objToString)
|
escapeRegExp(objToString)
|
||||||
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
|
.replace(/toString|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
|
||||||
);
|
);
|
||||||
@@ -1864,7 +1856,7 @@
|
|||||||
}
|
}
|
||||||
return typeof thisArg == 'undefined'
|
return typeof thisArg == 'undefined'
|
||||||
? property(func)
|
? property(func)
|
||||||
: baseMatchesProperty(func + '', thisArg);
|
: baseMatchesProperty(func, thisArg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -2477,23 +2469,40 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base implementation of `_.matchesProperty` which does not coerce `key`
|
* The base implementation of `_.matchesProperty` which does not which does
|
||||||
* to a string.
|
* not clone `value`.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} key The key of the property to get.
|
* @param {string} path The path of the property to get.
|
||||||
* @param {*} value The value to compare.
|
* @param {*} value The value to compare.
|
||||||
* @returns {Function} Returns the new function.
|
* @returns {Function} Returns the new function.
|
||||||
*/
|
*/
|
||||||
function baseMatchesProperty(key, value) {
|
function baseMatchesProperty(path, value) {
|
||||||
if (isStrictComparable(value)) {
|
var pathKey = path + '';
|
||||||
|
if (isKey(path) && isStrictComparable(value)) {
|
||||||
return function(object) {
|
return function(object) {
|
||||||
return object != null && object[key] === value &&
|
return object != null && object[pathKey] === value &&
|
||||||
(typeof value != 'undefined' || (key in toObject(object)));
|
(typeof value != 'undefined' || (pathKey in toObject(object)));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
path = toPath(path);
|
||||||
return function(object) {
|
return function(object) {
|
||||||
return object != null && baseIsEqual(value, object[key], null, true);
|
if (object == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var key = pathKey;
|
||||||
|
object = toObject(object);
|
||||||
|
if (!(key in object)) {
|
||||||
|
object = getPath(object, baseSlice(path, 0, -1));
|
||||||
|
if (object == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
key = last(path);
|
||||||
|
object = toObject(object);
|
||||||
|
}
|
||||||
|
return object[key] === value
|
||||||
|
? (typeof value != 'undefined' || (key in object))
|
||||||
|
: baseIsEqual(value, object[key], null, true);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2594,7 +2603,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The base implementation of `_.property` which does not coerce `key` to a string.
|
* The base implementation of `_.property` without support for deep paths.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @param {string} key The key of the property to get.
|
* @param {string} key The key of the property to get.
|
||||||
@@ -2606,15 +2615,22 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specialized version of `baseProperty` which supports deep paths.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {string} path The path of the property to get.
|
||||||
|
* @returns {Function} Returns the new function.
|
||||||
|
*/
|
||||||
function basePropertyDeep(path) {
|
function basePropertyDeep(path) {
|
||||||
var key = path + '';
|
var pathKey = path + '';
|
||||||
path = toPath(path);
|
path = toPath(path);
|
||||||
return function(object) {
|
return function(object) {
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return key in toObject(object)
|
return pathKey in toObject(object)
|
||||||
? object[key]
|
? object[pathKey]
|
||||||
: getPath(object, path);
|
: getPath(object, path);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -4224,8 +4240,16 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if `value` is a property name and not a property path.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} value The value to check.
|
||||||
|
* @returns {boolean} Returns `true` if `value` is a property name, else `false`.
|
||||||
|
*/
|
||||||
function isKey(value) {
|
function isKey(value) {
|
||||||
return typeof value == 'string' && !reDeepProp.test(value);
|
var type = typeof value;
|
||||||
|
return type == 'number' || (type == 'string' && !reIsDeepProp.test(value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -4524,12 +4548,22 @@
|
|||||||
return isObject(value) ? value : Object(value);
|
return isObject(value) ? value : Object(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts `value` to property path array if it is not one.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} value The value to process.
|
||||||
|
* @returns {Array} Returns the property path array.
|
||||||
|
*/
|
||||||
function toPath(value) {
|
function toPath(value) {
|
||||||
if (isArray(value)) {
|
if (isArray(value)) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
value = (value + '').replace(reBracketProp, replaceBracket);
|
var result = [];
|
||||||
return value.match(reProp) || [];
|
(value + '').replace(rePropName, function(match, key, number, quote, string) {
|
||||||
|
result.push(key || number || string);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -6827,13 +6861,13 @@
|
|||||||
}, function() { return [[], []]; });
|
}, function() { return [[], []]; });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of `key` from all elements in `collection`.
|
* Gets the value of `path` from all elements in `collection`.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @memberOf _
|
* @memberOf _
|
||||||
* @category Collection
|
* @category Collection
|
||||||
* @param {Array|Object|string} collection The collection to iterate over.
|
* @param {Array|Object|string} collection The collection to iterate over.
|
||||||
* @param {string} key The key of the property to pluck.
|
* @param {string} path The path of the property to pluck.
|
||||||
* @returns {Array} Returns the property values.
|
* @returns {Array} Returns the property values.
|
||||||
* @example
|
* @example
|
||||||
*
|
*
|
||||||
@@ -6849,8 +6883,8 @@
|
|||||||
* _.pluck(userIndex, 'age');
|
* _.pluck(userIndex, 'age');
|
||||||
* // => [36, 40] (iteration order is not guaranteed)
|
* // => [36, 40] (iteration order is not guaranteed)
|
||||||
*/
|
*/
|
||||||
function pluck(collection, key) {
|
function pluck(collection, path) {
|
||||||
return map(collection, property(key));
|
return map(collection, property(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -8847,9 +8881,9 @@
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (objToString.call(value) == funcTag) {
|
if (objToString.call(value) == funcTag) {
|
||||||
return reNative.test(fnToString.call(value));
|
return reIsNative.test(fnToString.call(value));
|
||||||
}
|
}
|
||||||
return isObjectLike(value) && (isHostObject(value) ? reNative : reHostCtor).test(value);
|
return isObjectLike(value) && (isHostObject(value) ? reIsNative : reIsHostCtor).test(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9407,8 +9441,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if `key` exists as a direct property of `object` instead of an
|
* Checks if `path` is a direct property.
|
||||||
* inherited property.
|
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @memberOf _
|
* @memberOf _
|
||||||
@@ -9424,18 +9457,13 @@
|
|||||||
* // => true
|
* // => true
|
||||||
*/
|
*/
|
||||||
function has(object, path) {
|
function has(object, path) {
|
||||||
if (object == null) {
|
var result = object != null && hasOwnProperty.call(object, path);
|
||||||
return false;
|
if (!result && !isKey(path)) {
|
||||||
|
path = toPath(path);
|
||||||
|
object = getPath(object, baseSlice(path, 0, -1));
|
||||||
|
result = object != null && hasOwnProperty.call(object, last(path));
|
||||||
}
|
}
|
||||||
if (hasOwnProperty.call(object, path)) {
|
return result;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (isKey(path)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
path = toPath(path);
|
|
||||||
object = getPath(object, baseSlice(path, 0, -1));
|
|
||||||
return hasOwnProperty.call(object, last(path));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9813,10 +9841,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolves the value of property `key` on `object`. If the value of `key` is
|
* Resolves the value of property `path` on `object`. If the value of `path`
|
||||||
* a function it is invoked with the `this` binding of `object` and its result
|
* is a function it is invoked with the `this` binding of `object` and its
|
||||||
* is returned, else the property value is returned. If the property value is
|
* result is returned, else the property value is returned. If the property
|
||||||
* `undefined` the `defaultValue` is used in its place.
|
* value is `undefined` the `defaultValue` is used in its place.
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @memberOf _
|
* @memberOf _
|
||||||
@@ -10375,7 +10403,7 @@
|
|||||||
radix = +radix;
|
radix = +radix;
|
||||||
}
|
}
|
||||||
string = trim(string);
|
string = trim(string);
|
||||||
return nativeParseInt(string, radix || (reHexPrefix.test(string) ? 16 : 10));
|
return nativeParseInt(string, radix || (reHasHexPrefix.test(string) ? 16 : 10));
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -11082,7 +11110,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a function which compares the property value of `key` on a given
|
* Creates a function which compares the property value of `path` on a given
|
||||||
* object to `value`.
|
* object to `value`.
|
||||||
*
|
*
|
||||||
* **Note:** This method supports comparing arrays, booleans, `Date` objects,
|
* **Note:** This method supports comparing arrays, booleans, `Date` objects,
|
||||||
@@ -11092,7 +11120,7 @@
|
|||||||
* @static
|
* @static
|
||||||
* @memberOf _
|
* @memberOf _
|
||||||
* @category Utility
|
* @category Utility
|
||||||
* @param {string} key The key of the property to get.
|
* @param {string} path The path of the property to get.
|
||||||
* @param {*} value The value to compare.
|
* @param {*} value The value to compare.
|
||||||
* @returns {Function} Returns the new function.
|
* @returns {Function} Returns the new function.
|
||||||
* @example
|
* @example
|
||||||
@@ -11105,8 +11133,8 @@
|
|||||||
* _.find(users, _.matchesProperty('user', 'fred'));
|
* _.find(users, _.matchesProperty('user', 'fred'));
|
||||||
* // => { 'user': 'fred' }
|
* // => { 'user': 'fred' }
|
||||||
*/
|
*/
|
||||||
function matchesProperty(key, value) {
|
function matchesProperty(path, value) {
|
||||||
return baseMatchesProperty(key + '', baseClone(value, true));
|
return baseMatchesProperty(path, baseClone(value, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user