103 lines
2.7 KiB
JavaScript
103 lines
2.7 KiB
JavaScript
|
/**
|
||
|
* Make sure properties are in an easily splittable format
|
||
|
*
|
||
|
* @private
|
||
|
* @param {String} props
|
||
|
* @param {String} [sep='.'] The default separator
|
||
|
* @return {String}
|
||
|
*/
|
||
|
function _normalizeProperty(props, sep = '.') {
|
||
|
// Since we split by period, and property lookup
|
||
|
// is the same by dot or [], replace bracket lookups
|
||
|
// with periods
|
||
|
return props.replace(/\[(.*?)]/g, sep + '$1');
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Tell if a nested object has a given property (or array a given index)
|
||
|
* given an object such as a.b.c.d = 5, hasNestedProperty(a, 'b.c.d') will return true.
|
||
|
*
|
||
|
* @param {Object} object the object to get the property from
|
||
|
* @param {String} property the path to the property as a string
|
||
|
* @returns {boolean} true when property in object, false otherwise
|
||
|
*/
|
||
|
export function hasNestedProperty(object, property) {
|
||
|
if (object && typeof object === 'object') {
|
||
|
if (typeof property === 'string' && property !== '') {
|
||
|
property = _normalizeProperty(property);
|
||
|
|
||
|
let split = property.split('.');
|
||
|
return split.reduce((obj, prop, idx, array) => {
|
||
|
if (idx === array.length - 1) {
|
||
|
return !!(obj && obj.hasOwnProperty(prop));
|
||
|
}
|
||
|
|
||
|
return obj && obj[prop];
|
||
|
}, object);
|
||
|
} else if (typeof property === 'number') {
|
||
|
return property in object;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the value of a deeply nested property in an object
|
||
|
*
|
||
|
* @param {Object} object the object to get the property
|
||
|
* @param {string} property the path to the property as a string
|
||
|
* @param {string} [sep='.'] The default separator to split on
|
||
|
* @return {*} the value of the property
|
||
|
*/
|
||
|
export function getNestedProperty(object, property, sep = '.') {
|
||
|
if (isType('string', property) && property !== '') {
|
||
|
// convert numbers to dot syntax
|
||
|
property = _normalizeProperty(property, sep);
|
||
|
const levels = property.split(sep);
|
||
|
|
||
|
try {
|
||
|
return levels.reduce((obj, prop) => obj[prop], object);
|
||
|
} catch (e) {
|
||
|
return undefined;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Reliably get the type of the value of a variable
|
||
|
*
|
||
|
* @param {*} x The variable to get the type of
|
||
|
* @return {string} The name of the type
|
||
|
*/
|
||
|
export function getType(x) {
|
||
|
// is it an array?
|
||
|
if (Array.isArray(x)) {
|
||
|
return 'array';
|
||
|
}
|
||
|
|
||
|
// Use typeof for truthy primitives
|
||
|
if (typeof x !== 'object') {
|
||
|
return (typeof x).toLowerCase();
|
||
|
}
|
||
|
|
||
|
const type = function () {
|
||
|
return Object.prototype.toString.call(this).slice(8, -1);
|
||
|
}
|
||
|
|
||
|
// Otherwise, strip the type out of the '[Object x]' toString value
|
||
|
return type.call(x).toLowerCase();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Check whether the value matches the passed type name
|
||
|
*
|
||
|
* @param {string} type Javascript type name
|
||
|
* @param {*} val The value to type check
|
||
|
* @return {boolean}
|
||
|
*/
|
||
|
export function isType(type, val) {
|
||
|
return getType(val) === String(type).toLowerCase();
|
||
|
}
|