UNPKG

@bortunac/matx

Version:

Precision-safe math utilities and nested object access for JavaScript.

270 lines (245 loc) 8.6 kB
// Safe Object.prototype.pointer const byte_units = ["b", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; if (!Object.prototype.pointer) { Object.defineProperty(Object.prototype, "pointer", { value: function (path, value) { if (!path) return undefined; const set = arguments.length === 2; const keys = path.split("."); let o = this; for (let i = 0; i < keys.length; i++) { const k = keys[i]; if (i === keys.length - 1) { if (set) o[k] = value; return o[k]; } if (!o.hasOwnProperty(k) || typeof o[k] !== "object") { if (set) o[k] = {}; else return undefined; } o = o[k]; } }, configurable: true, writable: true }); } // like map from array if (!Object.prototype.scan) { Object.defineProperty(Object.prototype, "scan", { value: function (cb) { return Object.entries(this).map(([k, v], idx) => { return cb.bind(this)(k, v, idx); }); }, configurable: true, writable: true, }); } Array.prototype.having && Object.defineProperty(Array.prototype, "having", { value: function (...A) { var selected = []; for (var i in A) { var o = A[i]; for (var v in this) { try { var bool = true, set = this[v]; if (typeof o === "string") { if (/\$@/.test(o)) { // Function -- DEEP SEARCH var str = o.replace(/(\$@)([^\s=<>!\)]*)/gim, function (p, i, j) { var a = j.match(/[^\.\[\]]+/gim); return " _x_" + (a.length ? ".pointer(" + JSON.stringify(a) + ")" : ""); }); bool = new Function("_x_", "return " + str)(set); } else { // eval with regex var str = o.replace(/\$\{(.*?)\}/gim, function (p, i) { return set[i]; }); bool = eval(str); } } else { if (this[v] === null) { bool = false; } else { for (var p in o) { if (typeof o[p] !== "object" ? this[v][p] != o[p] : !o[p].has(this[v][p])) { // p:1 <-> p:[1,2] !!!BAC>>> type_agnostic == not === bool = false; break; } } } } if (bool) { selected.push(this[v]); } } catch (e) { console.log("INPUT:", o, "\nCOND:", A, "\n", e); } } } return selected; }, configurable: true, writable: true }); // Define all Matx methods as non-enumerable properties on Math Object.defineProperties(Math, { scientific_to_num: { value(num) { if (/e/i.test(num)) { const [coeff, exp] = String(num).toLowerCase().split("e"); const e = +exp; const sign = e < 0 ? -1 : 1; const l = Math.abs(e); const parts = coeff.split("."); if (sign === -1) { return "0." + "0".repeat(l) + parts.join(""); } else { const decimals = parts[1] || ""; const zeros = l - decimals.length; return parts[0] + decimals + "0".repeat(zeros); } } return num; }, enumerable: false }, precision: { value(val, prec = 0, type = "round") { const p = Math.max(prec, 0); return (Math[type](parseFloat(val) * 10 ** p) / 10 ** p).toFixed(p); }, enumerable: false }, get_precision: { value(num) { const s = Math.scientific_to_num(String(num)).split("."); return s[1] ? s[1].length : 0; }, enumerable: false }, max_precision: { value(...args) { return args.reduce((m, x) => Math.max(m, Math.get_precision(x)), 0); }, enumerable: false }, sum: { value(...args) { let p = 0, t = 0; for (const x of args) { p = Math.max(p, Math.get_precision(x)); t += parseFloat(x) || 0; } return +Math.precision(t, p); }, enumerable: false }, diff: { value(a, b) { const p = Math.max(Math.get_precision(a), Math.get_precision(b)); return +Math.precision(a - b, p); }, enumerable: false }, acm: { // accumulate (add) value(acm, src, keys = []) { if (Array.isArray(src) && typeof src[0] === "object") { src.forEach(item => Math.acm(acm, item, keys)); return acm; } const K = Array.isArray(keys) ? keys : keys === "*" ? Object.keys(src) : [keys]; K.forEach(k => { const v = Math.sum(acm.pointer(k), src.pointer(k)); acm.pointer(k, v); }); return acm; }, enumerable: false }, acms: { // accumulate substact value(acm, src, keys = []) { if (Array.isArray(src) && typeof src[0] === "object") { src.forEach(item => Math.acms(acm, item, keys)); return acm; } const K = Array.isArray(keys) ? keys : keys === "*" ? Object.keys(src) : [keys]; K.forEach(k => { const v = Math.diff(acm.pointer(k), src.pointer(k)); acm.pointer(k, v); }); return acm; }, enumerable: false }, acmm: { // accumulate multiply value: function (acm, src, keys = []) { if (Array.isArray(src)) { // Handle array of objects/arrays if (typeof src[0] === "object" && src[0] !== null) { src.forEach((item) => Math.acmm(acm, item, keys)); return acm; } } const A = Array.isArray(keys) ? keys : keys === "*" ? Object.keys(src) : [keys]; A.forEach((key) => { const current = acm.pointer(key) || 1; const factor = src.pointer(key); if (typeof factor === "number") { acm.pointer(key, current * factor); } }); return acm; }, acmd: { // accumulate divide value(acm, src, keys = []) { if (Array.isArray(src) && typeof src[0] === "object") { src.forEach(item => Math.acmd(acm, item, keys)); return acm; } const K = Array.isArray(keys) ? keys : keys === "*" ? Object.keys(src) : [keys]; K.forEach(k => { const n = parseFloat(acm.pointer(k)) || 0; const d = parseFloat(src.pointer(k)) || 1; const r = d === 0 ? 0 : n / d; acm.pointer(k, +Math.precision(r, Math.get_precision(r))); }); return acm; }, enumerable: false }, float0: { value(x) { return parseFloat(typeof x === "string" ? x.replace(/,/g, "") : x) || 0; }, enumerable: false }, rad2deg: { value(r) { return (r * 180) / Math.PI; }, enumerable: false }, deg2rad: { value(d) { return (d * Math.PI) / 180; }, enumerable: false }, bytes: { value(x) { let i = 0, n = parseInt(x, 10) || 0; while (n >= 1024 && ++i) n = n / 1024; return n.toFixed(n < 10 && i > 0 ? 1 : 0) + " " + ["B", "KB", "MB", "GB", "TB"][i]; }, enumerable: false } });