@rxap/utilities
Version:
A collection of utility functions, types and interfaces.
264 lines • 12 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MergeDeepRight = MergeDeepRight;
exports.MergeDeepLeft = MergeDeepLeft;
exports.MergeArrayDeep = MergeArrayDeep;
exports.UseLeftArray = UseLeftArray;
exports.UseRightArray = UseRightArray;
exports.deepMerge = deepMerge;
const helpers_1 = require("./helpers");
/**
* Checks if the provided object has a specific property.
*
* @function _has
* @param {string} prop - The property name to check for in the object.
* @param {any} obj - The object to check for the property. This can be of any type that can have properties (e.g., objects, arrays, etc.).
* @returns {boolean} Returns true if the object has the specified property, false otherwise.
*
* @example
* // returns true
* _has('name', { name: 'John', age: 30 });
*
* @example
* // returns false
* _has('height', { name: 'John', age: 30 });
*
* @example
* // returns true
* _has('0', ['apple', 'banana', 'cherry']);
*
* @example
* // returns false
* _has('3', ['apple', 'banana', 'cherry']);
*/
function _has(prop, obj) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
/**
* The `mergeWithKey` function merges two string arrays into a single object, using the array indices as keys.
* If a key exists in both arrays, a custom merge function is applied to the values of the key in both arrays.
*
* @param {function} fn - A function that takes three string parameters: the key (K), the value from the left array (lk), and the value from the right array (rk).
* This function is applied when a key exists in both arrays and should return the value to be used in the result object for that key.
* @param {string[]} l - The left array to be merged.
* @param {string[]} r - The right array to be merged.
*
* @returns {Record<any, any>} - The resulting object after merging the two input arrays. The keys of the object are the indices of the arrays,
* and the values are either the value from the left array, the value from the right array, or the result of the custom merge function.
*
* @example
* // returns {0: 'a', 1: 'b', 2: 'c', 3: 'd'}
* mergeWithKey((k, lk, rk) => lk + rk, ['a', 'b'], ['c', 'd']);
*
* @throws {TypeError} - Throws a TypeError if the first argument is not a function or if the second and third arguments are not arrays of strings.
*/
function mergeWithKey(fn, l, r) {
const result = {};
let k;
for (k in l) {
if (_has(k, l)) {
result[k] = _has(k, r) ? fn(k, l[k], r[k]) : l[k];
}
}
for (k in r) {
if (_has(k, r) && !_has(k, result)) {
result[k] = r[k];
}
}
return result;
}
/**
* This function merges two objects deeply by applying a custom function to each key-value pair.
* The custom function is applied to the values of each key in both objects.
* If the value of a key in both objects is another object, the function is applied recursively.
*
* @param {function} fn - A function that takes three arguments:
* 1. K: The key of the current key-value pair.
* 2. lk: The value of the current key in the left object.
* 3. rk: The value of the current key in the right object.
* The function should return a value that will replace the current value in the merged object.
*
* @param {any} lObj - The left object to be merged. This object will not be modified.
*
* @param {any} rObj - The right object to be merged. This object will not be modified.
*
* @returns {any} - Returns a new object that is the result of deeply merging lObj and rObj.
* For each key, if the value in both objects is an object, the function is applied recursively.
* Otherwise, the custom function is applied to the values of the key in both objects.
*
* @example
* // returns { a: 1, b: { c: 3, d: 4 } }
* mergeDeepWithKey((k, l, r) => l + r, { a: 1, b: { c: 2 } }, { a: 0, b: { c: 1, d: 4 } });
*/
function mergeDeepWithKey(fn, lObj, rObj) {
return mergeWithKey(function (k, lVal, rVal) {
if ((0, helpers_1.isObject)(lVal) && (0, helpers_1.isObject)(rVal)) {
return mergeDeepWithKey(fn, lVal, rVal);
}
else {
return fn(k, lVal, rVal);
}
}, lObj, rObj);
}
/**
* Merges two objects deeply, giving priority to the properties of the second object (`rObj`) in case of a conflict.
*
* @param {any} lObj - The first object to be merged. This object's properties will be overridden by `rObj`'s properties in case of a conflict.
* @param {any} rObj - The second object to be merged. This object's properties will override `lObj`'s properties in case of a conflict.
* @param {MergeArrayFunction} mergeArrayFunction - The function used to merge arrays
*
* @returns {any} A new object that is the result of a deep merge of `lObj` and `rObj`. In case of a conflict, the properties of `rObj` will take precedence.
*
* @example
* // returns { a: 1, b: 2, c: 3 }
* mergeDeepRight({ a: 1, b: 2 }, { b: 3, c: 3 });
*
* @function MergeDeepRight
*/
function MergeDeepRight(lObj, rObj, mergeArrayFunction) {
return mergeDeepWithKey(function (k, lVal, rVal) {
if (Array.isArray(rVal)) {
if (Array.isArray(lVal)) {
return mergeArrayFunction(lVal, rVal, MergeDeepRight);
}
}
return rVal === undefined ? lVal : rVal;
}, lObj, rObj);
}
/**
* Merges two objects deeply, giving priority to the properties of the first object (`lObj`) in case of a conflict.
*
* @param {any} lObj - The first object to be merged. This object's properties will override `rObj`'s properties in case of a conflict.
* @param {any} rObj - The second object to be merged. This object's properties will be overridden by `lObj`'s properties in case of a conflict.
* @param {MergeArrayFunction} mergeArrayFunction - The function used to merge arrays
*
* @returns {any} A new object that is the result of a deep merge of `lObj` and `rObj`. In case of a conflict, the properties of `lObj` will take precedence.
*
* @example
* // returns { a: 1, b: 2, c: 3 }
* mergeDeepLeft({ a: 1, b: 2 }, { b: 3, c: 3 });
*
* @function MergeDeepLeft
*/
function MergeDeepLeft(lObj, rObj, mergeArrayFunction) {
return mergeDeepWithKey(function (k, lVal, rVal) {
if (Array.isArray(lVal)) {
if (Array.isArray(rVal)) {
return mergeArrayFunction(lVal, rVal, MergeDeepLeft);
}
}
return lVal === undefined ? rVal : lVal;
}, lObj, rObj);
}
/**
* Merges two arrays deeply by applying a custom merge function to elements at corresponding indices.
*
* This function creates a new array where each element is the result of a deep merge operation between
* elements from two input arrays at the same index. The merging of elements at each index is handled by
* a user-provided merge function. If an element exists in the second array but not in the first, the element
* from the second array is taken as is. If an element exists in both arrays, the merge function is applied.
*
* @param {any[]} a - The first array to merge.
* @param {any[]} b - The second array to merge. Elements in this array can overwrite or be merged with elements in the first array.
* @param {MergeFunction} mergeDeepFunction - A function that defines how two elements should be merged deeply. It should take two values and return the merged result.
* @returns {any[]} A new array containing the deeply merged elements of the two input arrays.
*
* @example
* const array1 = [{ name: "Alice" }, { name: "Bob" }];
* const array2 = [{ age: 25 }, { age: 30 }];
* const mergedArray = MergeArrayDeep(array1, array2, (x, y) => ({ ...x, ...y }));
* // mergedArray would be [{ name: "Alice", age: 25 }, { name: "Bob", age: 30 }]
*/
function MergeArrayDeep(a, b, mergeDeepFunction) {
const clone = a.slice();
for (let i = 0; i < Math.max(a.length, b.length); i++) {
if (b[i] !== undefined) {
if (a[i] === undefined) {
clone[i] = b[i];
}
else {
clone[i] = deepMerge(clone[i], b[i], mergeDeepFunction);
}
}
}
return clone;
}
/**
* Returns a shallow copy of the first array passed to the function.
*
* This function ignores the second array and the merge function provided as parameters.
* It simply returns a new array that is a shallow copy of the first array, meaning that
* the elements of the new array are exactly the same as the elements of the input array `a`.
* Changes to the elements of the returned array will reflect on the corresponding elements
* of the original array if those elements are objects.
*
* @param a - The array to be copied.
* @param b - This parameter is not used in the function.
* @param mergeDeepFunction - This parameter is not used in the function.
* @returns A new array that is a shallow copy of array `a`.
*/
function UseLeftArray(a, b, mergeDeepFunction) {
return a.slice();
}
/**
* Returns a shallow copy of the second array provided.
*
* This function ignores the first array and the merge function provided, and simply returns a new array that is a shallow copy of the second array. This means that the new array will have the same elements as the second array, but will be a different object in memory.
*
* @param a - The first array, which is not used in this function.
* @param b - The second array, from which a shallow copy is created and returned.
* @param mergeDeepFunction - A function intended for merging, which is not utilized in this function.
* @returns A new array that is a shallow copy of array `b`.
*/
function UseRightArray(a, b, mergeDeepFunction) {
return b.slice();
}
/**
* Merges two objects or arrays deeply, returning a new object or array that contains the combined contents of both.
* If the same key exists in both objects, the value from the second object (`b`) will be used.
* If the same index exists in both arrays, the value from the second array (`b`) will be used.
*
* @template T The type of the objects or arrays to be merged.
*
* @param {T} a The first object or array to merge. This will not be modified.
* @param {Partial<T> | RecursivePartial<T> | T} b The second object or array to merge. This will not be modified.
* @param {Function} mergeDeepFunction The function to use to merge nested objects or arrays.
*
* @returns {T} A new object or array that contains the combined contents of `a` and `b`.
*
* @throws {TypeError} If `a` or `b` is not an object or array.
*
* @example
*
* deepMerge({ a: 1, b: 2 }, { b: 3, c: 4 }); // Returns { a: 1, b: 3, c: 4 }
* deepMerge([1, 2], [2, 3]); // Returns [2, 3]
*
* @note
*
* - If `a` and `b` are both arrays, the returned array will have a length equal to the longer of the two input arrays.
* - If `a` and `b` are both objects, the returned object will contain all keys present in either `a` or `b`.
* - If `a` is an array and `b` is an object, or vice versa, a TypeError will be thrown.
* - This function uses recursion to merge objects and arrays deeply. Therefore, it may not be suitable for very large or deeply nested objects or arrays due to the risk of a stack overflow.
*
*/
function deepMerge(a, b, mergeDeepFunction = MergeDeepRight, mergeArrayFunction) {
if (!mergeArrayFunction) {
mergeArrayFunction = mergeDeepFunction === MergeDeepRight ? UseRightArray : UseLeftArray;
}
if (Array.isArray(a) || Array.isArray(b)) {
if (Array.isArray(a) && Array.isArray(b)) {
return mergeArrayFunction(a, b, mergeDeepFunction);
}
}
if (!(0, helpers_1.isObject)(a) || !(0, helpers_1.isObject)(b)) {
if (mergeDeepFunction === MergeDeepRight) {
return b;
}
else if (mergeDeepFunction === MergeDeepLeft) {
return a;
}
return b;
}
return mergeDeepFunction(a, b, mergeArrayFunction);
}
//# sourceMappingURL=deep-merge.js.map