camote-utils
Version:
A comprehensive TypeScript utility library featuring advanced string and number formatting, data structures, and algorithms
183 lines (182 loc) • 7.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.deepExclude = exports.deepMerge = exports.deepCompareObjects = exports.deepCompare = exports.deepSortAlphabetical = exports.deepClone = void 0;
const deepClone = (obj) => {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (Array.isArray(obj)) {
const arrCopy = [];
obj.forEach((item, index) => {
arrCopy[index] = (0, exports.deepClone)(item);
});
return arrCopy;
}
const objCopy = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
objCopy[key] = (0, exports.deepClone)(obj[key]);
}
}
return objCopy;
};
exports.deepClone = deepClone;
const deepSortAlphabetical = (input, inReverse = false) => {
if (Array.isArray(input)) {
return input
.map((item) => (0, exports.deepSortAlphabetical)(item))
.sort((a, b) => {
const aType = typeof a;
const bType = typeof b;
// Prioritize objects over numbers
if (aType === 'object' && bType !== 'object')
return inReverse ? 1 : -1;
if (bType === 'object' && aType !== 'object')
return inReverse ? -1 : 1;
// If both are of the same type, proceed with comparison
if (aType === bType) {
if (aType === 'string') {
return inReverse ? b.localeCompare(a) : a.localeCompare(b);
}
else if (aType === 'object') {
// Sort objects by their keys
const aKeys = Object.keys(a).sort();
const bKeys = Object.keys(b).sort();
return inReverse ? bKeys[0].localeCompare(aKeys[0]) : aKeys[0].localeCompare(bKeys[0]);
}
else {
// For numbers and other types, convert to string for comparison
return inReverse ? String(b).localeCompare(String(a)) : String(a).localeCompare(String(b));
}
}
// Handle cases where types differ
return inReverse ? bType.localeCompare(aType) : aType.localeCompare(bType);
});
}
else if (input && typeof input === 'object') {
return Object.keys(input)
.sort((a, b) => (inReverse ? b.localeCompare(a) : a.localeCompare(b)))
.reduce((acc, key) => {
acc[key] = (0, exports.deepSortAlphabetical)(input[key], inReverse);
return acc;
}, {});
}
else {
return input;
}
};
exports.deepSortAlphabetical = deepSortAlphabetical;
const deepCompare = (objectA, objectB, returnChanges = false) => {
const originalObj = (0, exports.deepSortAlphabetical)(objectA);
const toCompareObj = (0, exports.deepSortAlphabetical)(objectB);
// Handle null/undefined cases
if (originalObj === toCompareObj)
return returnChanges ? {} : true;
if (!originalObj || !toCompareObj) {
if (typeof originalObj === 'string' && toCompareObj === null)
return returnChanges ? originalObj : false;
if (typeof toCompareObj === 'string' && originalObj === null)
return returnChanges ? toCompareObj : false;
return returnChanges ? toCompareObj : false;
}
// For primitive types, do direct comparison
if (typeof originalObj === 'number' && typeof toCompareObj === 'number') {
return returnChanges ? (originalObj === toCompareObj ? {} : toCompareObj) : originalObj === toCompareObj;
}
// Get object types
const originalType = typeof originalObj;
const compareType = typeof toCompareObj;
// If types don't match, objects are different
if (originalType !== compareType)
return returnChanges ? toCompareObj : false;
// Handle array comparison
if (Array.isArray(originalObj) && Array.isArray(toCompareObj)) {
if (!returnChanges && originalObj.length !== toCompareObj.length)
return false;
const differences = [];
for (let i = 0; i < toCompareObj.length; i++) {
if (i >= originalObj.length) {
differences.push(toCompareObj[i]);
continue;
}
const compResult = (0, exports.deepCompare)(originalObj[i], toCompareObj[i], returnChanges);
if (returnChanges) {
if (compResult && (typeof compResult === 'object' ? Object.keys(compResult).length > 0 : true)) {
differences.push(toCompareObj[i]);
}
}
else if (!compResult) {
return false;
}
}
return returnChanges ? differences : true;
}
// Handle object comparison
if (originalType === 'object') {
const changes = {};
const originalKeys = Object.keys(originalObj);
const compareKeys = Object.keys(toCompareObj);
for (const key of compareKeys) {
if (!originalKeys.includes(key)) {
if (returnChanges) {
changes[key] = toCompareObj[key];
}
else {
return false;
}
}
else {
const compResult = (0, exports.deepCompare)(originalObj[key], toCompareObj[key], returnChanges);
if (returnChanges) {
if (compResult !== true &&
(typeof compResult !== 'object' ||
Object.keys(compResult).length > 0)) {
changes[key] = compResult;
}
}
else if (!compResult) {
return false;
}
}
}
return returnChanges ? changes : Object.keys(changes).length === 0;
}
// For primitive types, do direct comparison
const areEqual = originalObj === toCompareObj;
return returnChanges ? (areEqual ? {} : toCompareObj) : areEqual;
};
exports.deepCompare = deepCompare;
exports.deepCompareObjects = exports.deepCompare;
const deepMerge = (obj1, obj2, excluded = []) => {
const merged = { ...obj1 };
// Iterate over the keys of obj2
Object.keys(obj2).forEach(key => {
if (!excluded.includes(key)) {
if (typeof obj2[key] === 'object' && obj2[key] !== null) {
// If the property is an array, merge and remove duplicates
if (Array.isArray(obj2[key])) {
merged[key] = [...new Set([...(obj1[key] || []), ...(obj2[key] || [])])];
}
else {
merged[key] = (0, exports.deepMerge)(obj1[key] || {}, obj2[key], excluded);
}
}
else {
merged[key] = obj2[key];
}
}
});
return merged;
};
exports.deepMerge = deepMerge;
const deepExclude = (sourceArray, valuesToExclude, keySelector = (value) => JSON.stringify(value)) => {
const valuesToExcludeKeys = new Set(valuesToExclude.map((value) => keySelector(value)));
return sourceArray.filter((value) => {
const key = keySelector(value);
return !valuesToExcludeKeys.has(key);
});
};
exports.deepExclude = deepExclude;