UNPKG

@villedemontreal/general-utils

Version:
173 lines 6.32 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getCartesianProduct = void 0; exports.isEmpty = isEmpty; exports.isCollection = isCollection; exports.removeEmptyValues = removeEmptyValues; exports.removeMissingValues = removeMissingValues; exports.filter = filter; exports.toDictionary = toDictionary; exports.isMatching = isMatching; exports.isCompatible = isCompatible; const _ = require("lodash"); // prettier-ignore const getCartesianProduct = (...vectors) => _.reduce(vectors, (accumulator, vector) => _.flatten(_.map(accumulator, product => _.map(vector, value => product.concat([value])))), [[]] // accumulator, initially ); exports.getCartesianProduct = getCartesianProduct; /** * Tells whether the provided value is or can be considered empty. * * Following types are always considered *NOT* empty: boolean, number, function, RegExp. * * @example * • isEmpty('') → true * • isEmpty('…') → false * • isEmpty({}) → true * • isEmpty([]) → true * • isEmpty(null) → true * • isEmpty(undefined) → true * • isEmpty(true) → false * • isEmpty(/^$/) → false * * @param collection Collection to remove the empty values from. * @param #removeEmptyValues */ // Rationale: Lodash's `#isEmpty` only deals with collections... function isEmpty(value) { // Easy cases: let result = value === undefined || value === null || value === '' || undefined; const valueType = typeof value; // Some types just can't be "empty": if (result === undefined) { // tslint:disable-next-line:cyclomatic-complexity if (valueType === 'boolean' || valueType === 'number' || valueType === 'function' || value instanceof RegExp || value instanceof Date || value instanceof Error) { result = false; } } // Case of collections - leverage `_.size`: if (result === undefined && isCollection(value)) { result = _.isEmpty(value); } // Finally, if still not determined, return whether the value is "falsy": return result !== undefined ? result : !value; } /** Tells whether the provided value is or can be considered as a collection. */ function isCollection(value) { // Note: `Set` & `Map` instances are told to true by `_.isObjectLike` so no additional testing is required. return _.isObjectLike(value) || _.isArrayLike(value); } /** * Removes empty values from the given collection. * * @example * • removeEmptyValues({A: 'a', B: '', C: null, D: 'd', E: undefined, F: true}) → {A: 'a', D: 'd', F: true} * * @param collection Collection from which to remove the empty values. * * @see #isEmpty */ function removeEmptyValues(collection) { return filter(collection, (value) => !isEmpty(value)); } /** * Removes missing values from the given collection. * * @example * • removeMissingValues({A: 'a', B: '', C: null, D: 'd', E: undefined, F: true}) → {A: 'a', B: '', D: 'd', F: true} * * @param collection Collection from which to remove the missing values. */ function removeMissingValues(collection) { return filter(collection, (value) => !_.isNil(value)); } /** * Filters the given collection using the provided predicate. * * @param collection Collection from which to filter the values. */ function filter(collection, predicate) { let result = collection; if (!_.isNil(predicate)) { if (_.isArray(collection)) { result = _.filter(collection, predicate); } else { result = _.pickBy(collection, predicate); } } return result; } /** * Converts the provided collection into a dictionary. * * @param collection Collection to convert into a dictionary. * @param mapper Function used to make the keys of the resulting dictionary. */ function toDictionary(collection, mapper = (value, index) => String(index)) { return _.reduce(collection, (accumulator, value, index) => { const key = mapper(value, index); accumulator[key] = value; return accumulator; }, {}); } /** * Tells whether the provided model is matching with the expected model. * * @example * • isMatching({'A': 1, 'B': 2, 'C': 3}, {'A': 1, 'B': true}) // → false * • isMatching({'A': 1, 'B': true, 'C': 3}, {'A': 1, 'B': true}) // → true * * @param model Model to check. * @param expectedModel Structure composed of fixed values, describing what the model should be matching with. * @param keyFilter Keys of the fields to consider for the operation. */ function isMatching(model, expectedModel, keyFilter) { let _expectedModel = expectedModel; if (!isEmpty(keyFilter)) { _expectedModel = _.pick(expectedModel, keyFilter); } return _.isMatch(model, _expectedModel); } /** * Tells whether the provided model is compatible with the expected model. * * This method is very similar to `#isMatching` but also offers the ability to * specify rules (predicates) instead of fixed values only. * * @example * • isCompatible({'A': 1, 'B': 2, 'C': 3}, {'A': 1, 'B': _.isBoolean}) // → false * • isCompatible({'A': 1, 'B': true, 'C': 3}, {'A': 1, 'B': _.isBoolean}) // → true * * @param model Model to check. * @param expectedModel Structure composed of both fixed values and rules, describing what the model should be matching with. * @param keyFilter Keys of the fields to consider for the operation. */ function isCompatible(model, expectedModel, keyFilter) { const modelSubSet = {}; const _expectedModel = {}; const compatibilityRules = {}; let isCompatibleRulesEmpty = true; _.forEach(expectedModel, (value, key) => { if (isEmpty(keyFilter) || (keyFilter && keyFilter.indexOf(key) > -1)) { if (typeof value !== 'function') { _expectedModel[key] = value; } else { compatibilityRules[key] = value; modelSubSet[key] = model[key]; isCompatibleRulesEmpty = false; } } }); let result = _.isMatch(model, _expectedModel); if (!isCompatibleRulesEmpty) { result = result && _.conformsTo(modelSubSet, compatibilityRules); } return result; } //# sourceMappingURL=collectionUtils.js.map