nope/lib/helpers/objectMethods.ts
2020-10-26 08:39:34 +01:00

366 lines
9.4 KiB
TypeScript

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<T=any>(_data: any, _path: string, _default: any = _sentinel, _SPLITCHAR: string = SPLITCHAR): T | 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<any>();
} 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<string,any>}
*/
export function objectToMap(_obj: any): Map<string, any> {
/** Define the Returntype */
const _ret = new Map<string, any>();
/** 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<string, any>} The flatten Object
*/
export function flattenObject(data: any, prefix: string = '', splitchar: string = SPLITCHAR): Map<string, any> {
const _ret = new Map<string, any>();
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<string>();
/** 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<string, string>}
*/
export function flattenObjectType(data: any, prefix: string = ''): Map<string, string> {
const _ret = new Map<string, string>();
if (isObject(data)) {
recursiveForEach(data, prefix, (path, _data) => {
_ret.set(path, typeof _data);
});
}
return _ret;
}
/**
* Deflattens an Dict Based Object.
*
* @export
* @param {Map<string, any>} _flattenObject
* @returns {*}
*/
export function deflattenObject(_flattenObject: Map<string, any>): 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<T>(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');
}