import * as plugins from './smartobject.plugins'; const fastDeepEqual = plugins.fastDeepEqual; export { fastDeepEqual }; export interface IObjectCompareResult { presentInBothProperties: string[]; missingProperties: string[]; additionalProperties: string[]; nulledProperties: string[]; undefinedProperties: string[]; divergingProperties: string[]; equalProperties: string[]; } export const compareObjects = ( referenceObjectArg: any, comparisonObjectArg: any ): IObjectCompareResult => { const returnComparisonObject = { missingProperties: [] as string[], additionalProperties: [] as string[], presentInBothProperties: [] as string[], nulledProperties: [] as string[], undefinedProperties: [] as string[], divergingProperties: [] as string[], equalProperties: [] as string[], }; 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 if ( JSON.stringify(referenceObjectArg[currentProperty]) !== JSON.stringify(comparisonObjectArg[currentProperty]) ) { returnComparisonObject.divergingProperties.push(currentProperty); } // lets find equalProperties if ( JSON.stringify(referenceObjectArg[currentProperty]) === JSON.stringify(comparisonObjectArg[currentProperty]) ) { returnComparisonObject.equalProperties.push(currentProperty); } } for (const currentProperty of Object.keys(returnComparisonObject)) { const onlyUnique = (value: any, index: number, self: Array) => { return self.indexOf(value) === index; }; const uniqueArray = returnComparisonObject[currentProperty as keyof(typeof returnComparisonObject)].filter(onlyUnique); returnComparisonObject[currentProperty as keyof(typeof returnComparisonObject)] = uniqueArray; } return returnComparisonObject; }; /** * 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 = ( 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]); } };