node-vitals
Version:
Do more with less. A simple, high-performing, functional JavaScript library.
352 lines (311 loc) • 13 kB
JavaScript
/**
* -----------------------------------------------------------------------------
* VITALS METHOD: remap
* -----------------------------------------------------------------------------
* @section base
* @version 4.1.3
* @see [vitals.remap]{@link https://github.com/imaginate/vitals/wiki/vitals.remap}
*
* @author Adam Smith <adam@imaginate.life> (https://github.com/imaginate)
* @copyright 2017 Adam A Smith <adam@imaginate.life> (https://github.com/imaginate)
*
* Annotations:
* @see [JSDoc3](http://usejsdoc.org)
* @see [Closure Compiler JSDoc Syntax](https://developers.google.com/closure/compiler/docs/js-for-compiler)
*/
;
var newErrorMaker = require('./helpers/new-error-maker.js');
var splitKeys = require('./helpers/split-keys.js');
var escape = require('./helpers/escape.js');
var own = require('./helpers/own.js');
var copy = require('./copy.js');
var _is = require('./helpers/is.js');
////////////////////////////////////////////////////////////////////////////////
// VITALS METHOD: remap
////////////////////////////////////////////////////////////////////////////////
var remap = (function remapPrivateScope() {
//////////////////////////////////////////////////////////
// PUBLIC METHODS
// - remap
// - remap.object (remap.obj)
// - remap.array (remap.arr)
// - remap.string (remap.str)
//////////////////////////////////////////////////////////
/**
* A shortcut for making a new object/array/string by invoking an action over
* the values of an existing object/array/string.
*
* @public
* @param {!(Object|function|Array|string)} source
* @param {*} iteratee - Details per source type:
* - object: The iteratee must be a function with the optional params -
* value, key, source. Note this method lazily clones the source based on
* the iteratee's [length property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
* (i.e. if you alter the source object within the iteratee ensure to
* define the iteratee's third param so you can safely assume all
* references to the source are its original values).
* - array: The iteratee must be a function with the optional params -
* value, index, source. Note this method lazily slices the source based
* on the iteratee's [length property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
* (i.e. if you alter the source object within the iteratee ensure to
* define the iteratee's third param so you can safely assume all
* references to the source are its original values).
* - string: The iteratee must be a pattern to search for within the
* source. If the pattern is not a string or RegExp it will be converted
* to a string.
* @param {*=} replacement - Only use (and required) with string sources. If
* not a string or function the replacement is converted to a string. For
* details about using replacement functions see the
* [String.prototype.replace function param](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter).
* @param {Object=} thisArg - If thisArg is supplied the iteratee or
* replacement function is bound to its value.
* @return {!(Object|function|Array|string)}
*/
function remap(source, iteratee, replacement, thisArg) {
if ( _is.str(source) ) {
if (arguments.length < 2) throw _error('No iteratee defined');
if (arguments.length < 3) throw _error('No replacement defined');
if ( !_is.nil.un.obj(thisArg) ) throw _error.type('thisArg');
return _remapStr(source, iteratee, replacement, thisArg);
}
thisArg = replacement;
if ( !_is._obj(source) ) throw _error.type('source');
if ( !_is.func(iteratee) ) throw _error.type('iteratee');
if ( !_is.nil.un.obj(thisArg) ) throw _error.type('thisArg');
return _is._arr(source)
? _remapArr(source, iteratee, thisArg)
: _remapObj(source, iteratee, thisArg);
}
/**
* A shortcut for making a new object with the same keys and new values by
* invoking an action over the values of an existing object.
*
* @public
* @param {!(Object|function)} source
* @param {function(*=, string=, !(Object|function)=)} iteratee - The iteratee
* must be a function with the optional params - value, key, source. Note
* this method lazily clones the source based on the iteratee's
* [length property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
* (i.e. if you alter the source object within the iteratee ensure to define
* the iteratee's third param so you can safely assume all references to the
* source are its original values).
* @param {Object=} thisArg - If thisArg is supplied the iteratee is bound to
* its value.
* @return {!Object}
*/
remap.object = function remapObject(source, iteratee, thisArg) {
if ( !_is._obj(source) ) throw _error.type('source', 'object');
if ( !_is.func(iteratee) ) throw _error.type('iteratee', 'object');
if ( !_is.nil.un.obj(thisArg) ) throw _error.type('thisArg', 'object');
return _remapObj(source, iteratee, thisArg);
};
// define shorthand
remap.obj = remap.object;
/**
* A shortcut for making a new array by invoking an action over the values of
* an existing array-like object.
*
* @public
* @param {(!Object|function|string)} source - If source is a string it is
* converted to an array using one of the following values as the separator
* (values listed in order of rank):
* - `", "`
* - `","`
* - `"|"`
* - `" "`
* @param {function(*=, number=, !Array=)=} iteratee - The iteratee must be a
* function with the optional params - value, index, source. Note this
* method lazily slices the source based on the iteratee's [length property](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/length)
* (i.e. if you alter the source object within the iteratee ensure to define
* the iteratee's third param so you can safely assume all references to the
* source are its original values).
* @param {Object=} thisArg - If thisArg is supplied the iteratee is bound to
* its value.
* @return {!Array}
*/
remap.array = function remapArray(source, iteratee, thisArg) {
if ( _is.str(source) ) source = splitKeys(source);
if ( !_is._obj(source) ) throw _error.type('source', 'array');
if ( !_is.num(source.length) ) throw _error.type('source.length', 'array');
if ( !_is.func(iteratee) ) throw _error.type('iteratee', 'array');
if ( !_is.nil.un.obj(thisArg) ) throw _error.type('thisArg', 'array');
return _remapArr(source, iteratee, thisArg);
};
// define shorthand
remap.arr = remap.array;
/**
* A shortcut for [String.prototype.replace](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace)
* that defaults to global replacements instead of only the first.
*
* @public
* @param {string} source
* @param {*} pattern - If not a RegExp the pattern is converted to a string.
* @param {*} replacement - If not a string or function the replacement is
* converted to a string. For details about using replacement functions see
* [String.prototype.replace function param](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter).
* @param {Object=} thisArg - If thisArg is supplied the replacement function
* is bound to its value.
* @return {string}
*/
remap.string = function remapString(source, pattern, replacement, thisArg) {
if (arguments.length < 2) throw _error('No pattern defined', 'string');
if (arguments.length < 3) throw _error('No replacement defined', 'string');
if ( !_is.str(source) ) throw _error.type('source', 'string');
if ( !_is.nil.un.obj(thisArg) ) throw _error.type('thisArg', 'string');
return _remapStr(source, pattern, replacement, thisArg);
};
// define shorthand
remap.str = remap.string;
//////////////////////////////////////////////////////////
// PRIVATE METHODS - MAIN
//////////////////////////////////////////////////////////
/**
* @private
* @param {!(Object|function)} source
* @param {function(*, string=, !(Object|function)=)=} iteratee
* @param {Object=} thisArg
* @return {!Object}
*/
function _remapObj(source, iteratee, thisArg) {
/** @type {!Object} */
var obj;
/** @type {string} */
var key;
obj = {};
source = iteratee.length > 2 ? copy(source) : source;
iteratee = _is.undefined(thisArg) ? iteratee : _bindI(iteratee, thisArg);
switch (iteratee.length) {
case 0:
for (key in source) {
if ( own(source, key) ) obj[key] = iteratee();
}
break;
case 1:
for (key in source) {
if ( own(source, key) ) obj[key] = iteratee(source[key]);
}
break;
case 2:
for (key in source) {
if ( own(source, key) ) obj[key] = iteratee(source[key], key);
}
break;
default:
for (key in source) {
if ( own(source, key) ) obj[key] = iteratee(source[key], key, source);
}
}
return obj;
}
/**
* @private
* @param {!(Object|function)} source
* @param {function(*, number=, !Array=)=} iteratee
* @param {Object=} thisArg
* @return {!Array}
*/
function _remapArr(source, iteratee, thisArg) {
/** @type {!Array} */
var arr;
/** @type {number} */
var len;
/** @type {number} */
var i;
source = iteratee.length > 2 ? copy.arr(source) : source;
iteratee = _is.undefined(thisArg) ? iteratee : _bindI(iteratee, thisArg);
len = source.length;
arr = new Array(len);
i = -1;
switch (iteratee.length) {
case 0: while (++i < len) arr[i] = iteratee(); break;
case 1: while (++i < len) arr[i] = iteratee(source[i]); break;
case 2: while (++i < len) arr[i] = iteratee(source[i], i); break;
default: while (++i < len) arr[i] = iteratee(source[i], i, source);
}
return arr;
}
/**
* @private
* @param {string} source
* @param {*} pattern
* @param {*} replacement
* @param {Object=} thisArg
* @return {string}
*/
function _remapStr(source, pattern, replacement, thisArg) {
if (!source) return source;
if ( !_is.regex(pattern) ) {
pattern = String(pattern);
pattern = escape(pattern);
pattern = new RegExp(pattern, 'g');
}
replacement = _is.func(replacement)
? _is.undefined(thisArg)
? replacement
: _bindR(replacement, thisArg)
: String(replacement);
return source.replace(pattern, replacement);
}
//////////////////////////////////////////////////////////
// PRIVATE METHODS - GENERAL
//////////////////////////////////////////////////////////
/**
* @private
* @param {function} func
* @param {Object} thisArg
* @return {function}
*/
function _bindI(func, thisArg) {
switch (func.length) {
case 0:
return function iteratee() { return func.call(thisArg); };
case 1:
return function iteratee(val) { return func.call(thisArg, val); };
case 2:
return function iteratee(val, key) { return func.call(thisArg,val,key); };
}
return function iteratee(val, key, obj) {
return func.call(thisArg, val, key, obj);
};
}
/**
* @private
* @param {function} func
* @param {Object} thisArg
* @return {function}
*/
function _bindR(func, thisArg) {
switch (func.length) {
case 0: return function replacement() {
return func.call(thisArg);
};
case 1: return function replacement(match) {
return func.call(thisArg, match);
};
case 2: return function replacement(match, p1) {
return func.call(thisArg, match, p1);
};
case 3: return function replacement(match, p1, p2) {
return func.call(thisArg, match, p1, p2);
};
case 4: return function replacement(match, p1, p2, p3) {
return func.call(thisArg, match, p1, p2, p3);
};
case 5: return function replacement(match, p1, p2, p3, p4) {
return func.call(thisArg, match, p1, p2, p3, p4);
};
}
return function replacement() {
return func.apply(thisArg, arguments);
};
}
/**
* @private
* @type {!ErrorAid}
*/
var _error = newErrorMaker('remap');
//////////////////////////////////////////////////////////
// END OF PRIVATE SCOPE FOR REMAP
return remap;
})();
module.exports = remap;