2020-07-24 11:37:11 +00:00
|
|
|
import * as plugins from './smartobject.plugins';
|
|
|
|
|
2021-03-22 02:33:01 +00:00
|
|
|
const fastDeepEqual = plugins.fastDeepEqual;
|
2021-03-22 02:33:23 +00:00
|
|
|
export { fastDeepEqual };
|
2021-03-22 02:33:01 +00:00
|
|
|
|
2020-07-24 11:37:11 +00:00
|
|
|
export interface IObjectCompareResult {
|
|
|
|
presentInBothProperties: string[];
|
|
|
|
missingProperties: string[];
|
|
|
|
additionalProperties: string[];
|
|
|
|
nulledProperties: string[];
|
|
|
|
undefinedProperties: string[];
|
|
|
|
divergingProperties: string[];
|
|
|
|
equalProperties: string[];
|
|
|
|
}
|
|
|
|
|
2021-03-22 02:33:23 +00:00
|
|
|
export const compareObjects = (
|
|
|
|
referenceObjectArg: any,
|
|
|
|
comparisonObjectArg: any
|
|
|
|
): IObjectCompareResult => {
|
2020-07-24 11:37:11 +00:00
|
|
|
const returnComparisonObject = {
|
2022-03-06 10:09:53 +00:00
|
|
|
missingProperties: [] as string[],
|
|
|
|
additionalProperties: [] as string[],
|
|
|
|
presentInBothProperties: [] as string[],
|
|
|
|
nulledProperties: [] as string[],
|
|
|
|
undefinedProperties: [] as string[],
|
|
|
|
divergingProperties: [] as string[],
|
|
|
|
equalProperties: [] as string[],
|
2020-07-24 11:37:11 +00:00
|
|
|
};
|
2021-03-22 02:33:23 +00:00
|
|
|
|
2020-07-24 11:37:11 +00:00
|
|
|
const allProperties = Object.keys(referenceObjectArg).concat(Object.keys(comparisonObjectArg));
|
|
|
|
for (const currentProperty of allProperties) {
|
|
|
|
// lets find presentInBothProperties
|
|
|
|
if (referenceObjectArg[currentProperty] && comparisonObjectArg[currentProperty]) {
|
|
|
|
returnComparisonObject.presentInBothProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find missingProperties
|
|
|
|
if (referenceObjectArg[currentProperty] && !comparisonObjectArg[currentProperty]) {
|
|
|
|
returnComparisonObject.missingProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find additionalProperties
|
|
|
|
if (!referenceObjectArg[currentProperty] && comparisonObjectArg[currentProperty]) {
|
|
|
|
returnComparisonObject.additionalProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find nulledProperties
|
|
|
|
if (comparisonObjectArg[currentProperty] === null) {
|
|
|
|
returnComparisonObject.nulledProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find undefinedProperties
|
|
|
|
if (comparisonObjectArg[currentProperty] === undefined) {
|
|
|
|
returnComparisonObject.undefinedProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find divergingProperties
|
2021-03-22 02:33:23 +00:00
|
|
|
if (
|
|
|
|
JSON.stringify(referenceObjectArg[currentProperty]) !==
|
|
|
|
JSON.stringify(comparisonObjectArg[currentProperty])
|
|
|
|
) {
|
2020-07-24 11:37:11 +00:00
|
|
|
returnComparisonObject.divergingProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
|
|
|
|
// lets find equalProperties
|
2021-03-22 02:33:23 +00:00
|
|
|
if (
|
|
|
|
JSON.stringify(referenceObjectArg[currentProperty]) ===
|
|
|
|
JSON.stringify(comparisonObjectArg[currentProperty])
|
|
|
|
) {
|
2020-07-24 11:37:11 +00:00
|
|
|
returnComparisonObject.equalProperties.push(currentProperty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const currentProperty of Object.keys(returnComparisonObject)) {
|
2022-03-06 10:09:53 +00:00
|
|
|
const onlyUnique = (value: any, index: number, self: Array<any>) => {
|
2021-03-22 02:33:01 +00:00
|
|
|
return self.indexOf(value) === index;
|
|
|
|
};
|
2022-03-06 10:09:53 +00:00
|
|
|
const uniqueArray = returnComparisonObject[currentProperty as keyof(typeof returnComparisonObject)].filter(onlyUnique);
|
|
|
|
returnComparisonObject[currentProperty as keyof(typeof returnComparisonObject)] = uniqueArray;
|
2020-07-24 11:37:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return returnComparisonObject;
|
|
|
|
};
|
2022-03-06 10:09:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* adds an object to the parent object if it doesn't exists
|
|
|
|
* @param parentObject
|
|
|
|
* @param childParam
|
|
|
|
* @param logBool
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
export const smartAdd = (
|
|
|
|
parentObject: object,
|
|
|
|
childParam: string,
|
|
|
|
valueArg: any = {},
|
|
|
|
optionsArg?: {
|
|
|
|
interpretDotsAsLevel: boolean;
|
|
|
|
}
|
|
|
|
): typeof parentObject & any => {
|
|
|
|
optionsArg = {
|
|
|
|
interpretDotsAsLevel: true,
|
|
|
|
...optionsArg
|
|
|
|
};
|
|
|
|
|
|
|
|
let paramLevels: string[];
|
|
|
|
let referencePointer: any = parentObject;
|
|
|
|
if (optionsArg.interpretDotsAsLevel) {
|
|
|
|
paramLevels = childParam.split('.');
|
|
|
|
} else {
|
|
|
|
paramLevels = [childParam];
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let i = 0; i !== paramLevels.length; i++) {
|
|
|
|
const varName: string = paramLevels[i];
|
|
|
|
|
|
|
|
// is there a next variable ?
|
|
|
|
const varNameNext: string = (() => {
|
|
|
|
if (paramLevels[i + 1]) {
|
|
|
|
return paramLevels[i + 1];
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
})();
|
|
|
|
|
|
|
|
// build the tree in parentObject
|
|
|
|
if (!referencePointer[varName] && !varNameNext) {
|
|
|
|
referencePointer[varName] = valueArg;
|
|
|
|
referencePointer = null;
|
|
|
|
} else if (!referencePointer[varName] && varNameNext) {
|
|
|
|
referencePointer[varName] = {};
|
|
|
|
referencePointer = referencePointer[varName];
|
|
|
|
} else if (referencePointer[varName] && varNameNext) {
|
|
|
|
referencePointer = referencePointer[varName];
|
|
|
|
} else {
|
|
|
|
throw new Error('Something is strange!');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return parentObject;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* gets an object from the parent object using dots as levels by default
|
|
|
|
* @param parentObject
|
|
|
|
* @param childParam
|
|
|
|
* @param optionsArg
|
|
|
|
*/
|
|
|
|
export const smartGet = <T>(
|
|
|
|
parentObject: object,
|
|
|
|
childParam: string,
|
|
|
|
optionsArg?: {
|
|
|
|
interpretDotsAsLevel: boolean;
|
|
|
|
}
|
|
|
|
): T => {
|
|
|
|
optionsArg = {
|
|
|
|
interpretDotsAsLevel: true,
|
|
|
|
...optionsArg
|
|
|
|
};
|
|
|
|
|
|
|
|
let paramLevels: string[];
|
|
|
|
if (optionsArg.interpretDotsAsLevel) {
|
|
|
|
paramLevels = childParam.split('.');
|
|
|
|
} else {
|
|
|
|
paramLevels = [childParam];
|
|
|
|
}
|
|
|
|
|
|
|
|
let referencePointer: any = parentObject;
|
|
|
|
for (const level of paramLevels) {
|
|
|
|
if (referencePointer[level as any]) {
|
|
|
|
referencePointer = referencePointer[level as any];
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return referencePointer as T;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* checks if an object has a parameter with a given key name, returns true if yes.
|
|
|
|
* @param parentObject
|
|
|
|
* @param childParam
|
|
|
|
* @returns {boolean}
|
|
|
|
*/
|
|
|
|
export let exists = (parentObject: object, childParam: string): boolean => {
|
|
|
|
if (parentObject.hasOwnProperty(childParam)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* runs a function for all properties of an object whose key matches a regex expression
|
|
|
|
* @param parentObjectArg the parent object
|
|
|
|
* @param wildcardArg the rege expression to match the property keys against
|
|
|
|
* @param callbackArg the function to run with those properties
|
|
|
|
*/
|
|
|
|
export let forEachMinimatch = async (parentObjectArg: any, wildcardArg: string, callbackArg: (matchedArg: string) => void) => {
|
|
|
|
let propertyNames = Object.getOwnPropertyNames(parentObjectArg);
|
|
|
|
let propertyNamesMatched = propertyNames.filter(propertyNameArg => {
|
|
|
|
return plugins.minimatch(propertyNameArg, wildcardArg);
|
|
|
|
});
|
|
|
|
for (let propertyNameArg of propertyNamesMatched) {
|
|
|
|
await callbackArg(parentObjectArg[propertyNameArg]);
|
|
|
|
}
|
|
|
|
};
|