import util from 'util'; export type Obj = Record; // returns a copy of the given value, traversing into objects and arrays. export function simpleCopy(input: any): any { if (input === null || input === undefined) return input; const t = input.constructor; if (t === Object) { let ret: any = {} for (let k in input) ret[k] = simpleCopy(input[k]); return ret; } if (t === Array) { let ret: any[] = new Array(input.length); for (let i = 0; i < ret.length; i++) ret[i] = simpleCopy(input[i]); return ret; } if (t === Date) return new Date(input); return input; } // checks if both given values are the same, traversing into objects and arrays. export function simpleEquals(a: any, b: any): boolean { const ta = a === null || a === undefined ? a : a.constructor; const tb = b === null || b === undefined ? b : b.constructor; if (ta !== tb) return false; if (ta === Object) { if (Object.keys(a).length !== Object.keys(b).length) return false; for (const k in a) { if (!simpleEquals(a[k], b[k])) return false; } return true; } if (ta === Array) { if (a.length !== b.length) return false; for (let i = 0; i < a.length; i++) { if (!simpleEquals(a[i], b[i])) return false; } return true; } if (ta === Date) return a.getTime() === b.getTime(); return a === b; } // receives an object and a map of keys and returns an new object without all // the keys present in the given object but skips all keys that are present in // the given map. This is much faster than using delete on object keys. export const removeProperties = function (obj: Obj, keys: Obj): Obj { const out: Obj = {}; for (let k in obj) { if (!keys[k]) out[k] = obj[k]; } return out; } export const removeIndexes = function (arr: any[], indexes: number[]): any[] { let out: any[] = []; let j = 0; for (let i = 0; i < arr.length; i++) { if (i !== indexes[j]) out.push(arr[i]); else j++ } return out; } // removes empty objects and undefined values inside given value, after traversing // into objects and arrays, or returns the value itself. export const cleanStructure = function (x: any): any { if ( x === null || x === undefined || (x.constructor !== Object && x.constructor !== Array) ) return x; if (x.constructor === Object) { if (Object.keys(x).length === 0) return; let emptyKeys: Obj | undefined; for (let k in x) { x[k] = cleanStructure(x[k]); if (x[k] === undefined) { if (emptyKeys === undefined) emptyKeys = {}; emptyKeys[k] = true; } } if (emptyKeys !== undefined && Object.keys(emptyKeys).length > 0) { x = removeProperties(x, emptyKeys); if (Object.keys(x).length === 0) return; } } else if (x.constructor === Array) { if (x.length === 0) return; let emptyIndexes: number[] | undefined; for (let i = 0; i < x.length; i++) { x[i] = cleanStructure(x[i]); if (x[i] === undefined) { if (emptyIndexes === undefined) emptyIndexes = []; emptyIndexes.push(i); } } if (emptyIndexes !== undefined && emptyIndexes.length > 0) { x = removeIndexes(x, emptyIndexes); if (x.length === 0) return; } } return x; } // returns a string representing given value in the same format as nodejs REPL // prints return values to prompt. If 'colors' argument is true, the string will have // terminal specific code to make it colorful. export const pretty = function (x: any, colors?: boolean): String { return util.inspect(x, {depth: Infinity, colors: colors, breakLength: 100}); }