@beenotung/tslib
Version:
utils library in Typescript
294 lines (293 loc) • 8.71 kB
JavaScript
;
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;
}
}