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);
},
};