UNPKG

flavor-js

Version:

FlavorJS the definitive JS natives chainable extensions methods (based on lodash & ES6)

462 lines (437 loc) 13.2 kB
import _ from 'lodash'; import _baseIteratee from 'lodash/_baseIteratee'; import _basePullAll from 'lodash/_basePullAll'; import _toFinite from 'lodash/toFinite'; /** * @namespace lodash * @description all the mixins added to _ */ export default { /** * checks if a string is a percentage value<br><br> * @example <caption>eg. usage</caption> * var s = '23.97%'; * * console.log(_.isPercentage(s)); // true * * console.log(_.isPercentage('50%')); // true * * console.log(_.isPercentage(10)); // false * @memberOf lodash * @method isPercentage * @instance * @param {string} s - the string * @return {boolean} */ isPercentage(s) { return String.isString(s) && String.isPercentage(s); }, /** * parses float value in a percentage string<br><br> * @example <caption>eg. usage</caption> * var p = '50.5%'; * * console.log(_.parsePercentage(p)); // 50.5 * * console.log(_.parsePercentage('100%')); // 100 * * console.log(_.parsePercentage(25.3)); // null * @memberOf lodash * @method parsePercentage * @instance * @param {string} s - the percentage string * @return {null|number} */ parsePercentage(s) { if (String.isString(s) && String.isPercentage(s)) { return String.parsePercentage(s); } return null; }, /** * filters a collection with a list of values specified for one property<br><br> * @example <caption>eg. usage</caption> * var collection = [{ * id: 1, status: 'active' * }, { * id: 2, status: 'disabled' * }, { * id: 3, status: 'unactive' * }]; * * var allowedValues = ['active', 'unactive']; * * console.log(_.filterByValues(collection, 'status', allowedValues); * // logs [{id: 1, status: 'active'}, {id: 3, status: 'unactive'}] * @memberOf lodash * @method filterByValues * @instance * @param {Array|object} collection - the collection to filter * @param {string} key - the key to be used as property name * @param {Array} values - the list of values to check * @return {Array} */ filterByValues(collection, key, values) { return _.filter(collection, (o) => { return values.contains(o.path(key)); }); }, /** * deeply maps a recursive tree structure with (same structure) childrenPropName or 'children' property<br><br> * @example <caption>eg. usage</caption> * var tree = [{ * id: '1', status: 'enabled', items: [{ * id: '1.1', status: 'enabled', items: [{ * id: '1.1.1', status: 'enabled' * }, { * id: '1.1.2', status: 'disabled' * }] * }, { * id: '1.2', status: 'disabled' * }] * }]; * * console.log(_.deepMap(tree, 'items', function(treeItem) { * return { * id: treeItem.id, * status: treeItem.status, * combo: treeItem.id + '-' + treeItem.status * }; * }); * * // logs [{ * id: '1', status: 'enabled', combo: '1-enabled' items: [{ * id: '1.1', status: 'enabled', combo: '1.1-enabled', items: [{ * id: '1.1.1', status: 'enabled', combo: '1.1.1-enabled' * }, { * id: '1.1.2', status: 'disabled', combo: '1.1.2-disabled' * }] * }, { * id: '1.2', status: 'disabled', combo: '1.2-disabled' * }] * }] * @memberOf lodash * @method deepMap * @instance * @param {Array|object} collection - the collection to use for the deep mapping * @param {string} [childrenPropName='children'] - the property name to use for children collection * @param {function} mapCallback - the item mapping callback */ deepMap(collection, childrenPropName = 'children', mapCallback) { return _.map(collection, (item) => { if (!!item[childrenPropName]) { if (_.isArray(item[childrenPropName])) { item[childrenPropName] = _.deepMap(item[childrenPropName], childrenPropName, mapCallback); } } return mapCallback(item); }); }, /** * deeply searches in a recursive tree structure with (same structure) childrenPropName or 'children' property<br> * looking for an item with the propName === propValue<br><br> * @example <caption>eg. usage</caption> * var tree = [{ * id: '1', status: 'enabled', items: [{ * id: '1.1', status: 'enabled', items: [{ * id: '1.1.1', status: 'enabled' * }, { * id: '1.1.2', status: 'disabled' * }] * }, { * id: '1.2', status: 'disabled' * }] * }, { * id: '2', status: 'disabled', items: [{ * id: '2.1', status: 'enabled' * }, { * id: '2.2', status: 'enabled' * }] * }, { * id: '3', status: 'enabled', items: [{ * id: '3.1', status: 'disabled' * }, { * id: '3.2', status: 'enabled' * }, { * id: '3.3', status: 'enabled' * }] * }]; * * console.log(_.deepFindBy(tree, 'id', '1.1.1', 'items'); * // logs { * id: '1.1.1', status: 'enabled' * } * * console.log(_.deepFindBy(tree, function(item) { * return item.id === '3.2' * }, null, 'items'); * // logs { * id: '3.2', status: 'enabled' * } * @memberOf lodash * @method deepFindBy * @instance * @param {Array|object} collection - the collection * @param {string|function} propName - the property name or the predicate function to invoke (item will be passed as parameter to the predicate) * @param {*} propValue - the property value * @param {string} [childrenPropName='children'] - the children prop name * @return {*} */ deepFindBy(collection, propName, propValue, childrenPropName = 'children') { let found = null; collection.each((item) => { if (!found) { if (_.isFunction(propName)) { /** * use propName as predicate */ found = propName(item); } else if (item[propName] === propValue) { found = item; } else if (!!item[childrenPropName]) { if (_.isArray(item[childrenPropName])) { found = _.deepFindBy(item[childrenPropName], propName, propValue, childrenPropName); } } } }); return found; }, /** * deeply sorts a recursive tree structure with (same structure) childrenPropName or 'children' property<br><br> * @example <caption>eg. usage</caption> * var tree = [{ * id: '1', status: 'enabled', items: [{ * id: '1.1', status: 'enabled', items: [{ * id: '1.1.1', status: 'enabled' * }, { * id: '1.1.2', status: 'disabled' * }] * }, { * id: '1.2', status: 'disabled' * }] * }, { * id: '2', status: 'disabled', items: [{ * id: '2.1', status: 'enabled' * }, { * id: '2.2', status: 'enabled' * }] * }, { * id: '3', status: 'enabled', items: [{ * id: '3.1', status: 'disabled' * }, { * id: '3.2', status: 'enabled' * }, { * id: '3.3', status: 'enabled' * }] * }]; * * console.log(_.deepOrderBy(tree, ['id'], ['desc'], 'items'); * // logs [{ * id: '3', status: 'enabled', items: [{ * id: '3.3', status: 'enabled' * }, { * id: '3.2', status: 'disabled' * }, { * id: '3.1', status: 'enabled' * }] * }, { * id: '2', status: 'disabled', items: [{ * id: '2.2', status: 'enabled' * }, { * id: '2.1', status: 'enabled' * }] * }, { * id: '1', status: 'enabled', items: [{ * id: '1.2', status: 'disabled' * }, { * id: '1.1', status: 'enabled', items: [{ * id: '1.1.2', status: 'enabled' * }, { * id: '1.1.1', status: 'disabled' * }] * }] * }] * @memberOf lodash * @method deepOrderBy * @instance * @param {Array|object} collection - the collection * @param {Array|string} propNames - the list of property names to sort * @param {Array|string} propDirections - the list of order by direction to use with propNames * @param {string} [childrenPropName='children'] - the children prop name * @return {Array|object} */ deepOrderBy(collection, propNames, propDirections, childrenPropName = 'children') { if (_.isString(propNames)) { propNames = [propNames]; } if (!!propDirections) { if (_.isString(propDirections)) { propDirections = [propDirections]; } } else { propDirections = propNames.map(() => { return 'asc'; }); } collection = _.orderBy(collection, propNames, propDirections); collection.each((item) => { if (!!item[childrenPropName]) { if (_.isArray(item[childrenPropName])) { item[childrenPropName] = _.deepOrderBy(item[childrenPropName], propNames, propDirections, childrenPropName); } } }); return collection; }, /** * @todo document method * @memberOf lodash * @method pullAllByComparator * @instance * @param {collection} collection * @param {array} values * @param {function} comparator * @param {function} iteratee * @return {array} */ pullAllByComparator(collection, values, comparator, iteratee) { return (collection && collection.length && values && values.length) ? _basePullAll(collection, values, _baseIteratee(iteratee, 2), comparator) : collection; }, /** * a reverse implementation of _.times by lodash<br><br> * @example <caption>eg. usage</caption> * _.timesReverse(5, function(i) { * console.log(i); * }); * * // logs * 5 * 4 * 3 * 2 * 1 * @memberOf lodash * @method timesReverse * @instance * @param {number} times - num of times to invoke iteratee * @param {function} iteratee - the iteratee function to invoke<br> * the iteratee will be invoked passing che cycle indicator as i<br> * so the iteratee has to be something like this<br> * <pre> * function(i) {} * </pre> */ timesReverse(times, iteratee) { let index = times; while (--index >= 0) { _.isFunction(iteratee) && iteratee(index); } }, /** * an implementation of _.times by lodash, where you can specify start & end numbers<br><br> * @example <caption>eg. usage</caption> * _.timesRange(5, 10, function(i) { * console.log(i); * }); * * // logs * 5 * 6 * 7 * 8 * 9 * 10 * @example <caption>or</caption> * _.timesRange(5, 10, function(i) { * console.log(i); * }, true); * * // logs * 10 * 9 * 8 * 7 * 6 * 5 * @memberOf lodash * @method timesRange * @instance * @param {number} start - start num of times to invoke iteratee * @param {number} end - end num of times to invoke iteratee * @param {function} iteratee - the iteratee function to invoke<br> * the iteratee will be invoked passing che cycle indicator as i<br> * so the iteratee has to be something like this<br> * <pre> * function(i) {} * </pre> * @param {boolean} reverse - specify if you want reverse cycle */ timesRange(start, end, iteratee = null, reverse = false) { if (_.isFunction(iteratee)) { // Ensure the sign of `-0` is preserved. start = _toFinite(start); if (!end) { end = start; start = 0; } else { end = _toFinite(end); } let index = (reverse ? end : start); while ((reverse ? index-- >= start : index++ <= end)) { iteratee(index + (reverse ? 1 : -1)); } } }, objectToPaths(obj, leavesOnly = false, parentKey = null) { let result; if (_.isArray(obj)) { let idx = 0; result = _.flatMap(obj, (item) => { return _.objectToPaths(item, leavesOnly, (parentKey || '') + '[' + (idx++) + ']'); }); } else if (_.isPlainObject(obj)) { result = _.flatMap(_.keys(obj), (key) => { return _.map(_.objectToPaths(obj[key], leavesOnly, key), (subkey) => { return (parentKey ? parentKey + '.' : '') + subkey; }); }); } else { result = []; } return _.filter(_.sortBy(_.concat(result, parentKey || [])), (path) => { const value = _.get(obj, path); return !!leavesOnly ? !_.isArray(value) && !_.isPlainObject(value) && !_.isFunction(value) : true; }); }, keyValueToHash(keyValueCollection, keyField = 'key', valueField = 'value') { return _.transform(keyValueCollection, (result, keyValueItem) => { result[keyValueItem[keyField]] = keyValueItem[valueField]; }, {}); }, objectToHash(obj) { return _.transform(_.objectToPaths(obj, true), (result, path) => { result[path] = _.get(obj, path); }, {}); }, hashToObject(hash) { return _.transform(hash, (result, value, key) => { _.set(result, key, value); }, {}); }, cleanObject(o) { return _.omitBy(o, _.isFunction); }, mergeInc(...args) { return _.mergeWith.apply(this, args, (accValue, newValue) => { return accValue + newValue; }); }, sum(a, sumProp, startValue) { return _.reduce(a, (acc, item) => { return acc + item[sumProp]; }, startValue || 0); }, };