mapValues.js

/**
 * Should return new value basing on property name and value. Optionally may return exported REMOVE object to filter out
 * property from the object.
 *
 * @callback mapValuesFn
 * @param {*} value - property value
 * @param {string} key - property name
 * @example function fn(value, key) { return value * 5; } // all properties will be multiplied
 * @example function fn(value, key) {
 *   if (key === "name") {
 *     return REMOVE;
 *   }
 *   return value.toUpperCase();
 * }
 * // will upper case all properties and filter out `name` property
 * @returns {*}
 */

/**
 * Value that denotes that property should be removed
 *
 * @type {Object}
 */
const REMOVE = {};

/**
 * Iterates through object properties returning object with same properties but modified values. Optionally some
 * properties may be filtered out on returned object.
 *
 * @param {Object|Array} source - source object
 * @param {mapValuesFn} fn - map function callback that will return new value of a property
 * @example mapValues({ a: 1, b: 2 }, x => x * 2) // will return { a: 1, b: 4 }
 * @example mapValues({ a: 1, b: 2 }, () => REMOVE) // will return {}
 * @returns {Object|Array}
 */
const mapValues = (source, fn) => {
    const keys = Object.keys(source);

    return keys.reduce((result, key) => {
        const value = fn(source[key], key);
        if (value !== REMOVE) {
            result[key] = value; // eslint-disable-line no-param-reassign
        }
        return result;
    }, Array.isArray(source) ? [] : {});
};

export default mapValues;
export {
    REMOVE,
};