UNPKG

@beenotung/tslib

Version:
294 lines (293 loc) 8.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isNull = exports.updateObject = exports.SafeObjectOptions = void 0; exports.isObject = isObject; exports.hasFunction = hasFunction; exports.deepClone = deepClone; exports.deepEqual = deepEqual; exports.replaceObject = replaceObject; exports.createSafeObject = createSafeObject; exports.removeNull = removeNull; exports.ensureNonCyclic = ensureNonCyclic; exports.deleteUndefined = deleteUndefined; exports.objectToValues = objectToValues; exports.valuesToObject = valuesToObject; exports.objectGetOrSetDefault = objectGetOrSetDefault; exports.objectPick = objectPick; exports.incObject = incObject; const array_1 = require("./array"); const map_1 = require("./iterative/map"); const type_1 = require("./type"); function isObject(o) { return typeof o === 'object'; } function hasFunction(o, name) { return typeof o[name] === 'function'; } function deepClone(o) { if (!isObject(o)) { return o; } if (o instanceof Array) { return o.map(deepClone); } else { const res = {}; Object.keys(o).forEach(x => (res[x] = deepClone(o[x]))); return res; } } function deepEqual(a, b) { if (a === b) { return true; } const aType = (0, type_1.getObjectType)(a); const bType = (0, type_1.getObjectType)(b); if (aType !== bType) { return false; } switch (aType) { case 'AsyncFunction': case 'Function': return a.toString() === b.toString(); case 'Array': if (a.length !== b.length) { return false; } return a.every((_, i) => deepEqual(a[i], b[i])); case 'Uint8Array': if (a.length !== b.length) { return false; } return a.every((_, i) => deepEqual(a[i], b[i])); case 'Boolean': case 'Number': case 'String': return a === b; case 'Null': case 'Undefined': return true; case 'Map': { const aMap = a; const bMap = b; for (const key of aMap.keys()) { if (!bMap.has(key)) { return false; } if (!deepEqual(aMap.get(key), bMap.get(key))) { return false; } } return true; } case 'Set': { const aSet = a; const bSet = b; if (aSet.size !== bSet.size) { return false; } for (const value of aSet.values()) { if (!bSet.has(value)) { return false; } } return true; } case 'Date': return a.getTime() === b.getTime(); case 'Object': { const aKeys = Object.keys(a); const bKeys = Object.keys(b); if (aKeys.length !== bKeys.length) { return false; } return aKeys.every(key => deepEqual(a[key], b[key])); } default: throw Error('unsupported data type'); } } function replaceObject(dest, src) { Object.keys(dest).forEach(x => delete dest[x]); return Object.assign(dest, src); } const SafeObject = Symbol.for('SafeObject'); exports.SafeObjectOptions = { throwError: false, }; const safeProxyHandler = { get: (target, p, receiver) => { let value = Reflect.get(target, p, receiver); if (typeof p === 'symbol' || p === 'inspect') { return value; } if (exports.SafeObjectOptions.throwError && !Reflect.has(target, p)) { throw new TypeError(JSON.stringify(p) + ' is not defined in ' + target.toString()); } if (value === null || value === undefined) { value = createSafeObject(); target[p] = value; return value; } if (typeof value === 'object') { if (value[SafeObject] === true) { return value; } value = createSafeObject(value); target[p] = value; return value; } else { return value; } }, }; /** * make a loss object, very forgiving * */ function createSafeObject(target = {}) { target[SafeObject] = true; return new Proxy(target, safeProxyHandler); } const updateObject = (dest) => (x) => Object.assign(dest, x); exports.updateObject = updateObject; const isNull = (x) => !(x === null || x === undefined || x === ''); exports.isNull = isNull; function removeNull(o) { if (Array.isArray(o)) { return o .filter(x => !(x === null || x === undefined || x === '')) .map(x => removeNull(x)); } if (o instanceof Set) { return new Set(removeNull(Array.from(o))); } if (o instanceof Date) { return o; } if (typeof o === 'object' && o !== null) { o = Object.assign({}, o); for (const k of Object.keys(o)) { const v = o[k]; if (v === null || v === undefined || v === '') { delete o[k]; } } } return o; } /** * @param {any} o : value to be checked * @param {boolean} skip : true will remove duplicated value silently; * false will throw an error upon duplication * @param {any} placeholder : custom value to replace duplicated object * @param {function} mapper : optional map function * @param {Set} visited : internal book keeping seen objects * */ function ensureNonCyclic(o, skip = true, placeholder = void 0, mapper, visited = new Set()) { switch ((0, type_1.getObjectType)(o)) { case 'Number': case 'String': case 'Null': case 'Undefined': case 'Function': case 'AsyncFunction': /* these types can be duplicated */ return mapper ? mapper(o) : o; default: /* array, set, map, object */ if (visited.has(o)) { /* duplicated object */ if (skip) { return placeholder; } throw new Error('circular structure, duplicated value: ' + o); } /* non-duplicated object */ /* clone the set, to allow sibling duplication */ visited = (0, map_1.map_set)(visited, x => x); visited.add(o); return (0, map_1.map_any)(o, x => ensureNonCyclic(x, skip, placeholder, mapper, visited)); } } function deleteUndefined(o) { if (Array.isArray(o)) { (0, array_1.replaceArray)(o, o.filter(x => x !== undefined)); o.forEach(x => deleteUndefined(x)); return; } if (o instanceof Map) { o.forEach((value, key) => { if (value === undefined) { o.delete(key); } deleteUndefined(value); }); return; } if (typeof o === 'object') { for (const s of Object.keys(o)) { if (o[s] === undefined) { delete o[s]; continue; } deleteUndefined(o[s]); } return; } // e.g. number, string return; } /** * to encode json data in csv-style * i.e. only need to store the keys once for a large collection (e.g. Array / Set) * */ function objectToValues(o, keys = Object.keys(o).sort()) { return keys.map(key => o[key]); } /** * to decode json data from csv-style * i.e. populate values and keys to a collection (e.g. Array / Set) * */ function valuesToObject(values, keys) { const res = {}; if (values.length !== keys.length) { console.error('length of values and keys mismatch:', { keys: keys.length, values: values.length, }); throw new Error('invalid values'); } for (let i = 0; i < keys.length; i++) { const key = keys[i]; const value = values[i]; res[key] = value; } return res; } function objectGetOrSetDefault(object, key, f) { if (key in object) { return object[key]; } const res = f(); object[key] = res; return res; } function objectPick(object, keys) { const result = {}; for (const key of keys) { if (key in object) { result[key] = object[key]; } } return result; } function incObject(object, key) { const count = object[key]; if (count) { object[key] = count + 1; } else { object[key] = 1; } }