UNPKG

mauss

Version:

lightweight, modular, type-safe utilities

87 lines (86 loc) 3.46 kB
/** Augments the source object with various utility methods */ export function augment(object) { const o = clone(object); return { /** @returns a new object with all the keys passed */ build(keys) { return [...keys].reduce( // @ts-expect-error - unknown until `keys` are passed (a, k) => (Object.assign(Object.assign({}, a), { [k]: k in o ? o[k] : null })), {}); }, /** @returns an array of strongly-typed object entries */ get entries() { return Object.entries(o); }, /** @returns a new object with only the keys passed */ filter(keys) { const props = new Set(keys); // @ts-expect-error - unknown until `keys` are passed return iterate(o, ([k, v]) => props.has(k) && [k, v]); }, /** Deep freezes the current object */ freeze() { for (const key of Object.getOwnPropertyNames(o)) { const value = o[key]; if (value == null || typeof value !== 'object') continue; o[key] = augment(value).freeze(); } return Object.freeze(o); }, /** @returns an array of strongly-typed object keys */ get keys() { return Object.keys(o); }, /** @returns the total number of properties in the object */ get size() { return this.keys.length; }, }; } /** * Creating a copy of a data type, especially an object, is useful for removing the reference to the original object, keeping it clean from unexpected changes and side effects. This is possible because we are creating a new instance, making sure that any mutation or changes that are applied won't affect one or the other * @returns a deep copy of the object input */ export function clone(i) { if (!i || typeof i !== 'object') return i; if (Array.isArray(i)) return i.map(clone); const type = Object.prototype.toString.call(i); if (type !== '[object Object]') return i; return iterate(i); } /** * Iterate over the key-value pair of an object, returns a new object using the pairs returned from the callback function. If callback is omitted, the default behavior will create a deep copy of the original object * * The returned object will be filtered to only contain a key-value pair of the 2-tuple from `fn()`, any other values returned from the callback will be ignored, i.e. `void | Falsy` */ export function iterate(object, callback = ([k, v]) => [k, clone(v)]) { const pairs = Object.entries(object); const memo = []; for (let i = 0; i < pairs.length; i++) { const res = callback(pairs[i], i); if (!res || res.length !== 2) continue; memo.push(res); } return Object.fromEntries(memo); } /** Aggregates elements from each of the arrays and returns a single array of objects with the length of the largest array */ export function zip(...arrays) { const max = Math.max(...arrays.map((a) => a.length)); const items = []; for (let idx = 0, empty; idx < max; idx++, empty = !0) { const zipped = {}; for (const prime of arrays) { if (!prime[idx]) continue; empty = !Object.assign(zipped, prime[idx]); } if (!empty) items.push(zipped); } return items; }