export const SPLITCHAR = '.'; const _sentinel = new Object(); /** * Function to recurvely get an Attribute of the Object. * * @export * @param {*} _data * @param {string} _path * @param {*} [_default=_sentinel] * @returns {*} */ export function rgetattr(_data: any, _path: string, _default: any = _sentinel, _SPLITCHAR: string = SPLITCHAR): any | null { // Extract the Path let _obj = _data; if (_path.length > 0) { /** Check if there is a Substring available perform the normal method */ if (_path.indexOf(_SPLITCHAR) !== -1) { for (const attr of _path.split(_SPLITCHAR)) { /** Access a Map */ if (_obj instanceof Map) { _obj = _obj.get(attr); } else { /** Array or default Object */ _obj = _obj[attr]; } if ((_obj == null) && (_default === _sentinel)) { return null; } else if (_obj == null) { return _default; } } } else { /** Otherwise just return the Element */ return _obj[_path]; } } return _obj; } /** * Function to Set recursely a Attribute of an Object * * @export * @param {*} _data The Object, where the data should be stored * @param {string} _path The Path of the Attribute. All are seprated by a '.'! For Instance 'a.b.0.a.c' * @param {*} _value The Value which should be Stored in the Attribute. */ export function rsetattr(_data: any, _path: string, _value: any, _SPLITCHAR: string = SPLITCHAR): void { let _obj = _data; const _ptrs = _path.split(_SPLITCHAR); _ptrs.slice(0, -1).forEach(function (attr: string, idx: number) { // Adapt the Object by going through a loop let _sub = _obj[attr]; if (_sub === undefined || _sub === null) { // _obj is an Array and it doesnt contain the index // Extract the Next Element: const _next = _ptrs[idx + 1]; const _next_is_int = isInt(_next); if (Array.isArray(_obj)) { if (_next_is_int) { _obj[attr] = new Array(); } else { _obj[attr] = {}; } } else { if (_next_is_int) { _obj[attr] = []; } else { _obj[attr] = {}; } } _sub = _obj[attr]; } _obj = _sub; }); _obj[_ptrs[_ptrs.length - 1]] = _value; } /** * Checks whether the Value is an Integer * * @export * @param {*} value Value to be checked * @returns {boolean} Result */ export function isInt(value: any): boolean { return (parseInt(value) === value); } /** * Checks whether the Value is a Float * * @export * @param {*} value Value to be checked * @returns {boolean} Result */ export function isFloat(value: any): boolean { return (!isNaN(Number(value))); } /** * Copys the Object. Creates a Deep-Copy * of the Function * * @export * @param {*} value The value which should be copied * @returns {*} A Copy of the Value */ export function copy(value: any): any { // TODO RING // const _copy = {}; // /** Perform a Recursevly Foreach an Set an Attribute. */ // recursiveForEach(value, '', (path: string, _data: any) => { // rsetattr(_copy, path, _data); // }); // return _copy; return JSON.parse(JSON.stringify(value)); } /** * Function Converts a Object to a Map. * * @export * @param {*} _obj The Object which should be converted. * @returns {Map} */ export function objectToMap(_obj: any): Map { /** Define the Returntype */ const _ret = new Map(); /** Iterate through all properties of the Object */ for (const _prop of Object.getOwnPropertyNames(_obj)) { /** If isnt a function it could be added */ if (typeof _obj !== 'function') { _ret.set(_prop, _obj[_prop]); } } /** Return the Result */ return _ret; } /** * Checks whether the Value is an Object * * @export * @param {*} value Data to Test * @returns {boolean} Flag showing whether the Presented Data is an Object */ export function isObject(value: any): boolean { /** Verify whether the value contains some data. */ if (value) { if ((typeof value === 'object') && !Array.isArray(value)) { return Object.keys(value).length > 0; } } return false; } /** * Checks whether the Value is an Object * * @export * @param {*} value Data to Test * @returns {boolean} Flag showing whether the Presented Data is an Object */ export function isObjectOrArray(value: any): boolean { /** Verify whether the value contains some data. */ return isObject(value) || Array.isArray(value); } /** * Flattens an Object to a Map. * * For Instance: * * data = {a : { b : { c : 1, d: "hallo"}}} * * // Normal Call * res = flatteObject(data,'') * => res = {"a.b.c":1,"a.b.d":"hallo"} * * // With a Selected prefix 'additional.name' * res = flatteObject(data,'additional.name') * => res = {"additional.name.a.b.c":1,"additional.name.a.b.d":"hallo"} * * @export * @param {*} data The Data that should be converted * @param {string} [prefix=''] An additional prefix. * @returns {Map} The flatten Object */ export function flattenObject(data: any, prefix: string = '', splitchar: string = SPLITCHAR): Map { const _ret = new Map(); if (isObject(data) || Array.isArray(data)) { recursiveForEach(data, prefix, (path, _data) => { _ret.set(path, _data); }, splitchar, true); } return _ret; } /** * Function which is executed on each * * @export * @param {*} obj * @param {string} [prefix=''] * @param {(path: string, data: any) => void} dataCallback * @returns {*} */ export function recursiveForEach(obj: any, prefix: string = '', dataCallback: (path: string, data: any, parent?: string, level?: number) => void, _SPLITCHAR: string = SPLITCHAR, _callOnlyOnValues = true, _parent: string = '', _level = 0): any { /** Create an Array containing all Keys. */ let keys = Array(); /** Extract Keys of the Object, only if it isnt a string */ if (typeof obj !== 'string' && typeof obj !== 'function') { keys = Object.getOwnPropertyNames(obj); if (Array.isArray(obj)) { keys.splice(keys.indexOf('length'), 1); } } let called = false; if (!_callOnlyOnValues) { // Store the Element ! dataCallback(prefix, obj, _parent, _level); called = true; } // If there are Keys => It is a List or a Default Object if (keys.length > 0) { for (const _key of keys) { /** Var containing the Name */ const _str = (prefix === '') ? _key : prefix + _SPLITCHAR + _key; if (obj[_key] != null) { if (typeof obj[_key].toJSON === 'function') { const data = obj[_key].toJSON(); /** Recursive call the Function */ recursiveForEach(data, _str, dataCallback, _SPLITCHAR, _callOnlyOnValues, prefix, _level + 1); } else { /** Recursive call the Function */ recursiveForEach(obj[_key], _str, dataCallback, _SPLITCHAR, _callOnlyOnValues, prefix, _level + 1); } } } } else if (!called) { // Store the Element ! dataCallback(prefix, obj, prefix, _level); } } /** * Exports the used Types of an Object * * @export * @param {*} data The Data considered * @param {string} [prefix=''] * @returns {Map} */ export function flattenObjectType(data: any, prefix: string = ''): Map { const _ret = new Map(); if (isObject(data)) { recursiveForEach(data, prefix, (path, _data) => { _ret.set(path, typeof _data); }); } return _ret; } /** * Deflattens an Dict Based Object. * * @export * @param {Map} _flattenObject * @returns {*} */ export function deflattenObject(_flattenObject: Map): any { const _ret = {}; _flattenObject.forEach((_val: any, _key: string) => { rsetattr(_ret, _key, _val); }); return _ret; } /** * Function for deeply assigning * * @export * @param {*} target * @param {*} source * @returns */ export function deepAssign(target: any, source: any) { const flattend = flattenObject(source); for (const [path, value] of flattend.entries()) { rsetattr(target, path, value); } return target; } export function deepClone(obj: T) { let clone: any = Object.assign({}, obj); Object.keys(clone).forEach( key => (clone[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]) ); return (Array.isArray(obj) && obj.length ? (clone.length = obj.length) && Array.from(clone) : Array.isArray(obj) ? Array.from(obj) : clone) as T; } // export const deepEqual: (a: any, b: any) => boolean = require('deep-equal'); /** * Function to adapt the Object and only return a specific amount of elements. * @param obj The Object itself * @param properties a list of properties/pathes to keep */ export function keepPropertiesOfObject(obj: any, properties: { [index: string]: () => any }) { if (isObject(obj)) { const ret: any = {}; const defaultObj = { error: true }; // Iterate over the Properties, get the content of the path, clone it an put it to the // provided path Object.getOwnPropertyNames(properties).map(path => { const value = rgetattr(obj, path, defaultObj); rsetattr(ret, path, value !== defaultObj ? (typeof value === 'object' ? deepClone(value) : value) : properties[path]()); }); // Return the Object return ret; } // Wrong Datatype provided. throw TypeError('Function can only create Objects'); }