UNPKG

@rzl-zone/utils-js

Version:

A modern, lightweight set of JavaScript utility functions with TypeScript support for everyday development, crafted to enhance code readability and maintainability.

145 lines (140 loc) 7.29 kB
/*! * ==================================================== * Rzl Utils-JS. * ---------------------------------------------------- * Version: 3.11.0. * Author: Rizalvin Dwiky. * Repository: https://github.com/rzl-zone/utils-js. * ==================================================== */ import { NumberRangeUnion } from '@rzl-zone/ts-types-plus'; /** ---------------------------------------------------------------------- * * ***Utility: `findDuplicates`.*** * ---------------------------------------------------------------------- * **Finds duplicate values in an array by deep equality comparison.** * - **Behavior:** * - Uses ***`isEqual` utility function*** to compare elements * (handles objects, arrays, dates, NaN, etc.). * - Returns a new array containing only the *first occurrences* of duplicated values. * - Does **not mutate** the original array. * - Throws ***{@link TypeError | `TypeError`}*** if input is not an array. * @template T Type of elements in the input array. * @param {T[]} values - The array to check for duplicates. * @returns {T[]} An array of the duplicate values found in the input, * preserving order of their first duplicate appearance. * @throws **{@link TypeError | `TypeError`}** if the provided `values` argument is not an array. * @example * findDuplicates([1, 2, 2, 3, 4, 4]); * // ➔ [2, 4] * findDuplicates(["apple", "banana", "apple", "orange"]); * // ➔ ["apple"] * findDuplicates([{ a: 1 }, { a: 1 }, { a: 2 }]); * // ➔ [{ a: 1 }] * findDuplicates([NaN, NaN, 1]); * // ➔ [NaN] * findDuplicates([true, false, true]); * // ➔ [true] * findDuplicates([1, 2, 3]); * // ➔ [] */ declare const findDuplicates: <T>(values: T[]) => T[]; /** -------------------------------- * * ***Utility: `omitKeys`.*** * -------------------------------- * **This function creates a shallow copy of the given object omitting the * specified keys.** * - **Behavior:** * - It will return a new object without mutating the original. * - It also validates that ***`keysToOmit`*** does not contain duplicate keys. * - **ℹ️ Internally:** * - It uses ***`isEqual`*** to check for duplicates in * the ***`keysToOmit`*** array. * @template I The type of the input object. * @template K The keys to omit from the object. * @param {I} object - The source object to omit keys from. * @param {K[]} keysToOmit - An array of keys to exclude from the returned object. * @returns {Omit<I, K>} A new object without the specified keys. * @throws **{@link TypeError | `TypeError`}** if `keysToOmit` is not an array. * @throws **{@link Error | `Error`}** if duplicate keys are found in `keysToOmit`. * @example * omitKeys({ a: 1, b: 2, c: 3 }, ["b", "c"]); * //➔ { a: 1 } * omitKeys({ name: "John", age: 30 }, ["age"]); * //➔ { name: "John" } * omitKeys({ a: 1, b: 2 }, []); * //➔ { a: 1, b: 2 } (no changes) */ declare const omitKeys: <I extends Record<string, unknown>, K extends keyof I>(object: I, keysToOmit: K[]) => Omit<I, K>; type IndexArray = NumberRangeUnion<0, 30>; type DotPath<T, Prev extends string = ""> = T extends Array<infer U> ? DotPath<U, `${Prev}${Prev extends "" ? "" : "."}${IndexArray}`> : T extends object ? { [K in keyof T & string]: `${Prev}${Prev extends "" ? "" : "."}${K}` | DotPath<T[K], `${Prev}${Prev extends "" ? "" : "."}${K}`>; }[keyof T & string] : never; /** ------------------------------------------------------ * * ***Utility: `omitKeysDeep`.*** * ------------------------------------------------------ * **Recursively omits properties from an object using dot notation paths.** * - **Behavior:** * - Removes resulting empty objects (`{}`) and arrays (`[]`), cascading upwards * to remove empty parents until root if needed. * - **⚠️ Be careful:** * - If after omission an object or array becomes empty, it will be removed entirely * including all the way up to the root if necessary, resulting in `{}`. * - **ℹ️ Note:** * - For array indices, TypeScript autocomplete only suggests `0``30` * (to prevent editor lag on large unions). * However, higher indices are still fully supported at runtime — you can * manually type `"arr.99.key"` and it will work the same. * @template I - Type of the input object * @param {I} object * The object to process, should be a plain nested object or array structure. * @param {DotPath<I>[]} keysToOmit * An array of string paths in dot notation indicating the properties to remove, paths * can include numeric indices to target array elements, e.g. `"arr.0.x"` to * remove `x` from the first object inside the `arr` array. * @returns {Partial<I>} * A new deeply cloned object with the specified keys omitted, with resulting * empty objects or arrays fully removed (even if it collapses to `{}`). * @throws **{@link TypeError | `TypeError`}** if `keysToOmit` is not an array. * @throws **{@link Error | `Error`}** if `keysToOmit` contains duplicate paths. * @example * omitKeysDeep({ arr: [{ a: 1 }] }, ["arr.0.a"]); * // ➔ {} (array becomes empty and removed) * omitKeysDeep({ a: { b: { c: 1 }, d: 2 }, e: 3 }, ["a.b.c"]); * // ➔ { a: { d: 2 }, e: 3 } * omitKeysDeep({ a: [{ b: 1 }, { c: 2 }] }, ["a.0.b"]); * // ➔ { a: [{ c: 2 }] } * omitKeysDeep({ a: [{ b: 1 }] }, ["a.0.b"]); * // ➔ {} (array becomes empty and removed) * omitKeysDeep({ complex: [{ deep: [{ x: 1, y: 2 }] }] }, ["complex.0.deep.0.x"]); * // ➔ { complex: [{ deep: [{ y: 2 }] }] } * omitKeysDeep({ complex: [{ deep: [{ x: 1 }] }] }, ["complex.0.deep.0.x"]); * // ➔ {} (deep chain emptied and collapsed) * omitKeysDeep({ data: [[{ foo: 1, bar: 2 }]] }, ["data.0.0.foo"]); * // ➔ { data: [[{ bar: 2 }]] } * omitKeysDeep({ data: [[{ foo: 1 }]] }, ["data.0.0.foo"]); * // ➔ {} (nested arrays emptied completely) * omitKeysDeep({ x: [{ y: [{ z: 1 }, { w: 2 }] }] }, ["x.0.y.0.z"]); * // ➔ { x: [{ y: [{ w: 2 }] }] } * omitKeysDeep({ x: [{ y: [{ z: 1 }] }] }, ["x.0.y.0.z"]); * // ➔ {} (entire nested arrays removed) * omitKeysDeep({ p: { q: { r: 5 } }, s: 6 }, ["p.q.r"]); * // ➔ { s: 6 } (`p` removed because it becomes empty) * omitKeysDeep({ arr: [{ a: 1, b: 2 }, { c: 3 }] }, ["arr.0.a"]); * // ➔ { arr: [{ b: 2 }, { c: 3 }] } * omitKeysDeep({ root: [{ sub: [{ leaf: 10 }] }] }, ["root.0.sub.0.leaf"]); * // ➔ {} (deep nested arrays emptied to root) * omitKeysDeep({ meta: { tags: ["x", "y"], count: 2 } }, ["meta.count"]); * // ➔ { meta: { tags: ["x", "y"] } } * omitKeysDeep({ arr: [[{ a: 1 }, { b: 2 }]] }, ["arr.0.0.a"]); * // ➔ { arr: [[{ b: 2 }]] } * omitKeysDeep({ arr: [[{ a: 1 }]] }, ["arr.0.0.a"]); * // ➔ {} (double nested emptied) * omitKeysDeep({ nested: [{ list: [{ id: 1, val: 2 }] }] }, ["nested.0.list.0.val"]); * // ➔ { nested: [{ list: [{ id: 1 }] }] } * omitKeysDeep({ nested: [{ list: [{ id: 1 }] }] }, ["nested.0.list.0.id"]); * // ➔ {} (full collapse to empty) * omitKeysDeep({ mixed: { a: [1, 2, 3], b: { c: 4 } } }, ["mixed.b.c"]); * // ➔ { mixed: { a: [1, 2, 3] } } */ declare const omitKeysDeep: <I extends Record<string, unknown>>(object: I, keysToOmit: DotPath<I>[]) => Partial<I>; export { findDuplicates, omitKeys, omitKeysDeep };