2020-11-06 08:10:30 +00:00
|
|
|
/**
|
|
|
|
* @author Martin Karkowski
|
|
|
|
* @email m.karkowski@zema.de
|
|
|
|
* @desc [description]
|
|
|
|
*/
|
|
|
|
|
2021-11-14 22:16:07 +00:00
|
|
|
export const SPLITCHAR = "/";
|
2022-02-01 11:59:27 +00:00
|
|
|
import { deepEqual as _deepEqual } from "assert";
|
2022-07-07 07:14:22 +00:00
|
|
|
import { getLeastCommonPathSegment } from "./path";
|
|
|
|
import {
|
|
|
|
comparePatternAndPath,
|
|
|
|
containsWildcards,
|
|
|
|
MULTI_LEVEL_WILDCARD,
|
|
|
|
} from "./pathMatchingMethods";
|
2020-08-25 08:21:55 +00:00
|
|
|
|
|
|
|
const _sentinel = new Object();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Function to recurvely get an Attribute of the Object.
|
|
|
|
*
|
|
|
|
* @export
|
2022-07-07 07:14:22 +00:00
|
|
|
* @example data = [{a:1},{a:2}]; rgetattr(data, "0/a") -> 0; rgetattr(data,"hallo", "default") -> "default"
|
|
|
|
* @param {*} _data Data, where the item should be received
|
|
|
|
* @param {string} _path The path to extract
|
|
|
|
* @param {*} [_default=_sentinel] Default Object, if nothing else is provided
|
|
|
|
* @returns {*} The extracted data.
|
2020-08-25 08:21:55 +00:00
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function rgetattr<T = any>(
|
|
|
|
_data: any,
|
|
|
|
_path: string,
|
|
|
|
_default: any = _sentinel,
|
|
|
|
_SPLITCHAR: string = SPLITCHAR
|
|
|
|
): T | null {
|
2020-08-25 08:21:55 +00:00
|
|
|
// 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];
|
|
|
|
}
|
|
|
|
|
2022-01-21 15:17:40 +00:00
|
|
|
if ((_obj == null || _obj == undefined) && _default === _sentinel) {
|
2020-08-25 08:21:55 +00:00
|
|
|
return null;
|
2022-01-21 15:17:40 +00:00
|
|
|
} else if (_obj == null || _obj == undefined) {
|
2020-08-25 08:21:55 +00:00
|
|
|
return _default;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/** Otherwise just return the Element */
|
2022-01-21 15:17:40 +00:00
|
|
|
if (_obj[_path] == null || _obj[_path] == undefined) {
|
|
|
|
return _default;
|
|
|
|
}
|
|
|
|
|
2020-08-25 08:21:55 +00:00
|
|
|
return _obj[_path];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _obj;
|
|
|
|
}
|
|
|
|
|
2022-07-07 07:14:22 +00:00
|
|
|
/**
|
|
|
|
* Helper to query data from an object.
|
|
|
|
* @example data = [{a:1},{a:2}]; rqueryAttr(data, "+/a") -> [{path: "0/a", data: 0},{path: "1/a", data: 1}]
|
|
|
|
* @param data The data
|
|
|
|
* @param query The query to use.
|
|
|
|
* @returns Returns an array
|
|
|
|
*/
|
|
|
|
export function rqueryAttr<T>(
|
|
|
|
data: any,
|
|
|
|
query: string
|
|
|
|
): {
|
|
|
|
path: string;
|
|
|
|
data: T;
|
|
|
|
}[] {
|
|
|
|
if (!containsWildcards(query)) {
|
|
|
|
const _sentinel = {
|
|
|
|
id: Date.now(),
|
|
|
|
};
|
|
|
|
const extractedData = rgetattr<T>(data, query, _sentinel);
|
|
|
|
|
|
|
|
if (extractedData === (_sentinel as any)) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return [{ path: query, data: extractedData }];
|
|
|
|
}
|
|
|
|
|
|
|
|
let ret: {
|
|
|
|
path: string;
|
|
|
|
data: T;
|
|
|
|
}[] = [];
|
|
|
|
|
|
|
|
const multiLevel = query.includes(MULTI_LEVEL_WILDCARD);
|
|
|
|
// Determine the max depth
|
|
|
|
const maxDepth = multiLevel ? Infinity : query.split(SPLITCHAR).length;
|
|
|
|
|
|
|
|
// get the flatten object
|
|
|
|
const map = flattenObject(data, {
|
|
|
|
maxDepth,
|
|
|
|
onlyPathToSimpleValue: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Iterate over the items and use our
|
|
|
|
// path matcher to extract the matching items.
|
|
|
|
for (const [path, value] of map.entries()) {
|
|
|
|
const r = comparePatternAndPath(query, path);
|
|
|
|
|
|
|
|
if (r.affectedOnSameLevel || (multiLevel && r.affectedByChild)) {
|
|
|
|
ret.push({
|
|
|
|
path,
|
|
|
|
data: value,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Helper to query data from an object.
|
2022-08-16 07:46:57 +00:00
|
|
|
*
|
2022-08-02 20:56:08 +00:00
|
|
|
* props is defined as followed:
|
|
|
|
* ```typescript
|
|
|
|
* props: {
|
|
|
|
* key: string;
|
|
|
|
* query: string;
|
|
|
|
* }[]
|
|
|
|
* ```
|
|
|
|
*
|
2022-08-16 07:46:57 +00:00
|
|
|
* @example Example 1:
|
|
|
|
*
|
2022-08-02 20:56:08 +00:00
|
|
|
* ```javascript
|
2022-08-16 07:46:57 +00:00
|
|
|
*
|
2022-08-02 20:56:08 +00:00
|
|
|
* const data = { "deep": { "nested": "test" } };
|
|
|
|
* const result = convert_data(data, [
|
|
|
|
* {
|
|
|
|
* "key": "result",
|
|
|
|
* "query": "deep/nested",
|
|
|
|
* },
|
|
|
|
* ]);
|
2022-08-16 07:46:57 +00:00
|
|
|
*
|
2022-08-02 20:56:08 +00:00
|
|
|
* // ==> result = [{"result": "test"}]
|
2022-08-16 07:46:57 +00:00
|
|
|
* ```
|
2022-08-02 20:56:08 +00:00
|
|
|
*
|
|
|
|
* @example Example 2:
|
2022-08-16 07:46:57 +00:00
|
|
|
*
|
2022-08-02 20:56:08 +00:00
|
|
|
* ```javascript
|
|
|
|
* data = {
|
|
|
|
* "array": [
|
|
|
|
* {
|
|
|
|
* "data1": 0,
|
|
|
|
* "data2": "a",
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* "data1": 1,
|
|
|
|
* "data2": "a",
|
|
|
|
* },
|
|
|
|
* ],
|
|
|
|
* "not": { "nested": "hello" }
|
|
|
|
* }
|
|
|
|
*
|
|
|
|
* let result = convert_data(data, [
|
|
|
|
* {
|
|
|
|
* "key": "a",
|
|
|
|
* "query": "array/+/data1",
|
|
|
|
* },
|
|
|
|
* {
|
|
|
|
* "key": "b",
|
|
|
|
* "query": "array/+/data2",
|
|
|
|
* },
|
|
|
|
* ])
|
|
|
|
*
|
|
|
|
* // ==> result = [{"a": 0, "b": "a"}, {"a": 1, "b": "a"}]
|
2022-08-16 07:46:57 +00:00
|
|
|
* ```
|
|
|
|
*
|
2022-07-07 07:14:22 +00:00
|
|
|
* @param data The data
|
|
|
|
* @param query The query to use.
|
|
|
|
* @returns Returns an array
|
|
|
|
*/
|
|
|
|
export function convertData<T>(
|
|
|
|
data: any,
|
|
|
|
props: {
|
|
|
|
key: string;
|
|
|
|
query: string;
|
|
|
|
}[]
|
|
|
|
): T[] {
|
|
|
|
const ret: {
|
|
|
|
[index: string]: {
|
|
|
|
path: string;
|
|
|
|
data: any;
|
|
|
|
}[];
|
|
|
|
} = {};
|
|
|
|
|
|
|
|
const commonPattern = getLeastCommonPathSegment(
|
2022-07-23 05:34:38 +00:00
|
|
|
props.map((item) => {
|
|
|
|
return item.query;
|
|
|
|
})
|
2022-07-07 07:14:22 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
if (!commonPattern) {
|
|
|
|
throw Error("No common pattern has been found");
|
|
|
|
}
|
|
|
|
|
|
|
|
props.map((prop) => {
|
|
|
|
ret[prop.key] = rqueryAttr(data, prop.query);
|
|
|
|
});
|
|
|
|
|
|
|
|
const helper: { [index: string]: { [index: string]: any } } = {};
|
|
|
|
|
|
|
|
for (const prop of props) {
|
|
|
|
// get the item
|
|
|
|
const items = ret[prop.key];
|
|
|
|
|
|
|
|
for (const item of items) {
|
|
|
|
const result = comparePatternAndPath(commonPattern, item.path);
|
|
|
|
|
|
|
|
if (result.pathToExtractData) {
|
|
|
|
if (helper[result.pathToExtractData] === undefined) {
|
|
|
|
helper[result.pathToExtractData] = {};
|
|
|
|
}
|
|
|
|
helper[result.pathToExtractData][prop.key] = item.data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-25 05:36:40 +00:00
|
|
|
return Object.getOwnPropertyNames(helper).map((key) => {
|
|
|
|
return helper[key] as T;
|
|
|
|
});
|
2022-07-07 07:14:22 +00:00
|
|
|
}
|
|
|
|
|
2020-08-25 08:21:55 +00:00
|
|
|
/**
|
|
|
|
* Function to Set recursely a Attribute of an Object
|
|
|
|
*
|
2021-08-17 15:52:46 +00:00
|
|
|
* @author M.Karkowski
|
2020-08-25 08:21:55 +00:00
|
|
|
* @export
|
|
|
|
* @param {*} _data The Object, where the data should be stored
|
2022-08-02 20:56:08 +00:00
|
|
|
* @param {string} _path The Path of the Attribute. All are seprated by a the splitchar. (Defaults to'.' => For Instance 'a/b/0/a/c')
|
2020-08-25 08:21:55 +00:00
|
|
|
* @param {*} _value The Value which should be Stored in the Attribute.
|
2022-08-02 20:56:08 +00:00
|
|
|
* @param {string} [_SPLITCHAR=SPLITCHAR] The Splitchar to use. Defaults to "/"
|
2020-08-25 08:21:55 +00:00
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function rsetattr(
|
|
|
|
_data: any,
|
|
|
|
_path: string,
|
|
|
|
_value: any,
|
|
|
|
_SPLITCHAR: string = SPLITCHAR
|
|
|
|
): void {
|
2020-08-25 08:21:55 +00:00
|
|
|
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 {
|
2021-03-22 19:25:15 +00:00
|
|
|
return parseInt(value) === value;
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks whether the Value is a Float
|
|
|
|
*
|
|
|
|
* @export
|
|
|
|
* @param {*} value Value to be checked
|
|
|
|
* @returns {boolean} Result
|
|
|
|
*/
|
|
|
|
export function isFloat(value: any): boolean {
|
2021-03-22 19:25:15 +00:00
|
|
|
return !isNaN(Number(value));
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function copy<T>(value: T): T {
|
2020-08-25 08:21:55 +00:00
|
|
|
// 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 */
|
2021-03-22 19:25:15 +00:00
|
|
|
if (typeof _obj !== "function") {
|
2020-08-25 08:21:55 +00:00
|
|
|
_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) {
|
2021-03-22 19:25:15 +00:00
|
|
|
if (typeof value === "object" && !Array.isArray(value)) {
|
2020-08-25 08:21:55 +00:00
|
|
|
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
|
2022-07-07 07:14:22 +00:00
|
|
|
* res = flatteObject(data)
|
2020-08-25 08:21:55 +00:00
|
|
|
* => res = {"a.b.c":1,"a.b.d":"hallo"}
|
|
|
|
*
|
|
|
|
* // With a Selected prefix 'additional.name'
|
2022-07-07 07:14:22 +00:00
|
|
|
* res = flatteObject(data,{prefix:'additional.name'})
|
2020-08-25 08:21:55 +00:00
|
|
|
* => 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
|
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function flattenObject(
|
|
|
|
data: any,
|
2021-08-17 15:52:46 +00:00
|
|
|
options: {
|
2021-12-04 07:25:26 +00:00
|
|
|
prefix?: string;
|
|
|
|
splitchar?: string;
|
|
|
|
maxDepth?: number;
|
|
|
|
onlyPathToSimpleValue?: boolean;
|
|
|
|
} = {}
|
2021-03-22 19:25:15 +00:00
|
|
|
): Map<string, any> {
|
2021-12-04 07:25:26 +00:00
|
|
|
const _options = Object.assign(
|
|
|
|
{
|
|
|
|
prefix: "",
|
|
|
|
splitchar: SPLITCHAR,
|
|
|
|
onlyPathToSimpleValue: false,
|
|
|
|
maxDepth: Infinity,
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
2021-08-17 15:52:46 +00:00
|
|
|
|
2020-08-25 08:21:55 +00:00
|
|
|
const _ret = new Map<string, any>();
|
|
|
|
|
|
|
|
if (isObject(data) || Array.isArray(data)) {
|
2021-03-22 19:25:15 +00:00
|
|
|
recursiveForEach(
|
|
|
|
data,
|
2021-08-17 15:52:46 +00:00
|
|
|
_options.prefix,
|
2021-03-22 19:25:15 +00:00
|
|
|
(path, _data) => {
|
|
|
|
_ret.set(path, _data);
|
|
|
|
},
|
2021-08-17 15:52:46 +00:00
|
|
|
_options.splitchar,
|
2021-11-14 22:16:07 +00:00
|
|
|
_options.onlyPathToSimpleValue,
|
2021-08-17 15:52:46 +00:00
|
|
|
_options.maxDepth
|
2021-03-22 19:25:15 +00:00
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
return _ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 07:25:26 +00:00
|
|
|
* Function, that will iterate over an object.
|
2021-08-17 15:52:46 +00:00
|
|
|
* It will call the callback on every element.
|
2021-12-04 07:25:26 +00:00
|
|
|
*
|
2020-08-25 08:21:55 +00:00
|
|
|
*
|
2021-08-17 15:52:46 +00:00
|
|
|
* @author M.Karkowski
|
2020-08-25 08:21:55 +00:00
|
|
|
* @export
|
2021-08-17 15:52:46 +00:00
|
|
|
* @param {*} obj The Object to iterate
|
|
|
|
* @param {string} [prefix=""] A prefix for the Path.
|
|
|
|
* @param {(
|
|
|
|
* path: string,
|
|
|
|
* data: any,
|
|
|
|
* parent?: string,
|
|
|
|
* level?: number
|
|
|
|
* ) => void} dataCallback Callback, that will be called.
|
|
|
|
* @param {string} [_SPLITCHAR=SPLITCHAR] The Splitchar to use, to generate the path
|
2021-12-04 07:25:26 +00:00
|
|
|
* @param {boolean} [_callOnlyOnValues=true] A Flag, to start the
|
2021-08-17 15:52:46 +00:00
|
|
|
* @param {*} [_maxDepth=Infinity] Determine the max Depth, after which the Iteration will be stopped.
|
|
|
|
* @param {string} [_parent=""] For Recursive call only
|
|
|
|
* @param {number} [_level=0] For Recursive call only
|
|
|
|
* @return {*} {*}
|
2020-08-25 08:21:55 +00:00
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function recursiveForEach(
|
|
|
|
obj: any,
|
|
|
|
prefix = "",
|
|
|
|
dataCallback: (
|
|
|
|
path: string,
|
|
|
|
data: any,
|
|
|
|
parent?: string,
|
|
|
|
level?: number
|
|
|
|
) => void,
|
|
|
|
_SPLITCHAR: string = SPLITCHAR,
|
|
|
|
_callOnlyOnValues = true,
|
2021-08-17 15:52:46 +00:00
|
|
|
_maxDepth = Infinity,
|
2021-03-22 19:25:15 +00:00
|
|
|
_parent = "",
|
|
|
|
_level = 0
|
|
|
|
): any {
|
2021-11-14 22:16:07 +00:00
|
|
|
if (_level > _maxDepth) {
|
2021-08-17 15:52:46 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an Array with the Keys.
|
2020-08-25 08:21:55 +00:00
|
|
|
let keys = Array<string>();
|
|
|
|
|
2021-12-04 07:25:26 +00:00
|
|
|
// Extract the keys of an object, but only if it isnt a
|
2021-08-17 15:52:46 +00:00
|
|
|
// string or function.
|
2021-03-22 19:25:15 +00:00
|
|
|
if (typeof obj !== "string" && typeof obj !== "function") {
|
2020-08-25 08:21:55 +00:00
|
|
|
keys = Object.getOwnPropertyNames(obj);
|
|
|
|
if (Array.isArray(obj)) {
|
2021-03-22 19:25:15 +00:00
|
|
|
keys.splice(keys.indexOf("length"), 1);
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
2021-08-17 15:52:46 +00:00
|
|
|
// Define the variable, containing the path
|
2021-03-22 19:25:15 +00:00
|
|
|
const _str = prefix === "" ? _key : prefix + _SPLITCHAR + _key;
|
2020-08-25 08:21:55 +00:00
|
|
|
|
|
|
|
if (obj[_key] != null) {
|
2021-12-04 07:25:26 +00:00
|
|
|
// Test if there exist a specific function, which will convert the
|
2021-08-17 15:52:46 +00:00
|
|
|
// Object to JSON => if so, we use that function, otherwise we will
|
|
|
|
// just proceed.
|
2021-03-22 19:25:15 +00:00
|
|
|
if (typeof obj[_key].toJSON === "function") {
|
2020-08-25 08:21:55 +00:00
|
|
|
const data = obj[_key].toJSON();
|
2021-08-17 15:52:46 +00:00
|
|
|
// Recursive call this function.
|
2021-03-22 19:25:15 +00:00
|
|
|
recursiveForEach(
|
|
|
|
data,
|
|
|
|
_str,
|
|
|
|
dataCallback,
|
|
|
|
_SPLITCHAR,
|
|
|
|
_callOnlyOnValues,
|
2021-08-17 15:52:46 +00:00
|
|
|
_maxDepth,
|
2021-03-22 19:25:15 +00:00
|
|
|
prefix,
|
|
|
|
_level + 1
|
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
} else {
|
2021-08-17 15:52:46 +00:00
|
|
|
// Recursive call this function.
|
2021-03-22 19:25:15 +00:00
|
|
|
recursiveForEach(
|
|
|
|
obj[_key],
|
|
|
|
_str,
|
|
|
|
dataCallback,
|
|
|
|
_SPLITCHAR,
|
|
|
|
_callOnlyOnValues,
|
2021-08-17 15:52:46 +00:00
|
|
|
_maxDepth,
|
2021-03-22 19:25:15 +00:00
|
|
|
prefix,
|
|
|
|
_level + 1
|
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (!called) {
|
|
|
|
// Store the Element !
|
|
|
|
dataCallback(prefix, obj, prefix, _level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 07:25:26 +00:00
|
|
|
* Exports the used Types of an Object. The result is the
|
2021-08-17 15:52:46 +00:00
|
|
|
* a Map, where the key represents the path and the value
|
|
|
|
* represents the type of the element (stored in the path)
|
2021-12-04 07:25:26 +00:00
|
|
|
*
|
2021-08-17 15:52:46 +00:00
|
|
|
* @author M.Karkowski
|
2020-08-25 08:21:55 +00:00
|
|
|
* @export
|
2021-08-17 15:52:46 +00:00
|
|
|
* @param {*} data The Data to check
|
|
|
|
* @param {{
|
|
|
|
* prefix?: string,
|
|
|
|
* splitchar?: string,
|
|
|
|
* maxDepth?: number,
|
|
|
|
* }} [options={}]
|
|
|
|
* @return {*} {Map<string, string>} key = path; value = type of element;
|
2020-08-25 08:21:55 +00:00
|
|
|
*/
|
2021-12-04 07:25:26 +00:00
|
|
|
export function flattenObjectType(
|
|
|
|
data: any,
|
|
|
|
options: {
|
|
|
|
prefix?: string;
|
|
|
|
splitchar?: string;
|
|
|
|
onlyPathToSimpleValue?: boolean;
|
|
|
|
maxDepth?: number;
|
|
|
|
} = {}
|
|
|
|
): Map<string, string> {
|
2021-08-17 15:52:46 +00:00
|
|
|
// Options which will be used
|
2021-12-04 07:25:26 +00:00
|
|
|
const _options = Object.assign(
|
|
|
|
{
|
|
|
|
prefix: "",
|
|
|
|
onlyPathToSimpleValue: false,
|
|
|
|
splitchar: SPLITCHAR,
|
|
|
|
maxDepth: Infinity,
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
2021-08-17 15:52:46 +00:00
|
|
|
|
2020-08-25 08:21:55 +00:00
|
|
|
const _ret = new Map<string, string>();
|
|
|
|
|
|
|
|
if (isObject(data)) {
|
2021-12-04 07:25:26 +00:00
|
|
|
recursiveForEach(
|
|
|
|
data,
|
|
|
|
_options.prefix,
|
|
|
|
(path, _data) => {
|
|
|
|
_ret.set(path, typeof _data);
|
|
|
|
},
|
|
|
|
_options.splitchar,
|
|
|
|
_options.onlyPathToSimpleValue,
|
|
|
|
_options.maxDepth
|
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
return _ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-12-04 07:25:26 +00:00
|
|
|
* Deflattens an Dict Based Object. The Object it self is represented
|
2021-08-17 15:52:46 +00:00
|
|
|
* as Map, whereas the Key represents the path.
|
|
|
|
*
|
2020-08-25 08:21:55 +00:00
|
|
|
*
|
2021-08-17 15:52:46 +00:00
|
|
|
* @author M.Karkowski
|
2020-08-25 08:21:55 +00:00
|
|
|
* @export
|
|
|
|
* @param {Map<string, any>} _flattenObject
|
2021-08-17 15:52:46 +00:00
|
|
|
* @return {*} {*}
|
2020-08-25 08:21:55 +00:00
|
|
|
*/
|
2021-12-04 07:25:26 +00:00
|
|
|
export function deflattenObject(
|
|
|
|
_flattenObject: Map<string, any>,
|
|
|
|
options: {
|
|
|
|
prefix?: string;
|
|
|
|
splitchar?: string;
|
|
|
|
}
|
|
|
|
): any {
|
2021-08-17 15:52:46 +00:00
|
|
|
// Options which will be used
|
2021-12-04 07:25:26 +00:00
|
|
|
const _options = Object.assign(
|
|
|
|
{
|
|
|
|
prefix: "",
|
|
|
|
splitchar: SPLITCHAR,
|
|
|
|
},
|
|
|
|
options
|
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
const _ret = {};
|
|
|
|
|
|
|
|
_flattenObject.forEach((_val: any, _key: string) => {
|
2021-08-17 15:52:46 +00:00
|
|
|
// if there is a prefix, remove it:
|
|
|
|
if (_options.prefix !== "") {
|
|
|
|
_key = _key.slice(_options.prefix.length);
|
|
|
|
}
|
|
|
|
rsetattr(_ret, _key, _val, _options.splitchar);
|
2020-08-25 08:21:55 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-08-17 15:52:46 +00:00
|
|
|
/**
|
|
|
|
* Function to deeply clone the given object.
|
|
|
|
*
|
|
|
|
* @author M.Karkowski
|
|
|
|
* @export
|
|
|
|
* @template T
|
|
|
|
* @param {T} obj
|
|
|
|
* @return {*} {T}
|
|
|
|
*/
|
|
|
|
export function deepClone<T>(obj: T): T {
|
2021-11-14 22:16:07 +00:00
|
|
|
switch (typeof obj) {
|
|
|
|
case "object": {
|
2022-01-25 19:43:17 +00:00
|
|
|
if (obj === null) {
|
|
|
|
return null;
|
|
|
|
}
|
2021-11-14 22:16:07 +00:00
|
|
|
const clone: any = Object.assign({}, obj);
|
2022-07-25 05:36:40 +00:00
|
|
|
Object.keys(clone).forEach((key) => {
|
|
|
|
clone[key] =
|
|
|
|
typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
|
|
|
|
});
|
2021-12-04 07:25:26 +00:00
|
|
|
return (
|
|
|
|
Array.isArray(obj) && obj.length
|
|
|
|
? (clone.length = obj.length) && Array.from(clone)
|
|
|
|
: Array.isArray(obj)
|
2021-11-14 22:16:07 +00:00
|
|
|
? Array.from(obj)
|
2021-12-04 07:25:26 +00:00
|
|
|
: clone
|
|
|
|
) as T;
|
2021-11-14 22:16:07 +00:00
|
|
|
}
|
|
|
|
default: {
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
}
|
2020-08-25 08:21:55 +00:00
|
|
|
}
|
|
|
|
|
2022-02-01 11:59:27 +00:00
|
|
|
/**
|
|
|
|
* Compares deep a and b (using assert)
|
|
|
|
* @param a Object A
|
|
|
|
* @param b Object B
|
|
|
|
* @returns The Result
|
|
|
|
*/
|
|
|
|
export function deepEqual<T>(a: T, b: T): boolean {
|
|
|
|
try {
|
|
|
|
_deepEqual(a, b);
|
|
|
|
return true;
|
|
|
|
} catch (e) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2020-08-25 08:21:55 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2021-03-22 19:25:15 +00:00
|
|
|
export function keepPropertiesOfObject(
|
|
|
|
obj: any,
|
|
|
|
properties: { [index: string]: () => any }
|
2021-08-17 15:52:46 +00:00
|
|
|
): any {
|
2020-08-25 08:21:55 +00:00
|
|
|
if (isObject(obj)) {
|
|
|
|
const ret: any = {};
|
|
|
|
|
|
|
|
const defaultObj = { error: true };
|
|
|
|
|
2021-03-22 19:25:15 +00:00
|
|
|
// Iterate over the Properties, get the content of the path, clone it an put it to the
|
2020-08-25 08:21:55 +00:00
|
|
|
// provided path
|
2021-03-22 19:25:15 +00:00
|
|
|
Object.getOwnPropertyNames(properties).map((path) => {
|
2020-08-25 08:21:55 +00:00
|
|
|
const value = rgetattr(obj, path, defaultObj);
|
2021-03-22 19:25:15 +00:00
|
|
|
rsetattr(
|
|
|
|
ret,
|
|
|
|
path,
|
|
|
|
value !== defaultObj
|
|
|
|
? typeof value === "object"
|
|
|
|
? deepClone(value)
|
|
|
|
: value
|
|
|
|
: properties[path]()
|
|
|
|
);
|
2020-08-25 08:21:55 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// Return the Object
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wrong Datatype provided.
|
2021-03-22 19:25:15 +00:00
|
|
|
throw TypeError("Function can only create Objects");
|
|
|
|
}
|