UNPKG

daily-toolset

Version:

A lightweight, versatile collection of TypeScript utility functions for everyday development needs. Simplify and streamline your Node.js, React, and Next.js projects with a powerful suite of well-organized helpers for strings, arrays, dates, objects, and

250 lines (249 loc) 8.06 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.transformObject = transformObject; exports.pickFromObject = pickFromObject; exports.omitFromObject = omitFromObject; exports.prependToObjectKey = prependToObjectKey; exports.deepMerge = deepMerge; exports.jsonify = jsonify; const dateUtils_1 = require("./dateUtils"); /** * Transforms a flat object with dot-separated keys into a nested object structure. * * The function takes an object with keys that may contain dot notation to represent * nested structures and returns a new object with the corresponding nested structure. * If a key part is numeric, it will be treated as an array index. * * @param {ObjectTransformInput} obj - The input object with flat, dot-separated keys. * @returns {Transformed} - The transformed object with nested structure. * * @throws {Error} Throws an error if the input is not an object or if the key structure is invalid. * * @example * const input = { * "a.b": 1, * "a.c": 2, * "d": 3, * "e.0": 4, * "e.1": 5 * }; * const result = transformObject(input); * console.log(result); * // Output: { a: { b: 1, c: 2 }, d: 3, e: [4, 5] } */ function transformObject(obj) { if (!obj || typeof obj !== "object") { throw new Error("Invalid input: Expected an object"); } const result = {}; Object.keys(obj).forEach((key) => { const value = obj[key]; const parts = key.split("."); let current = result; for (let i = 0; i < parts.length; i++) { const part = parts[i]; if (i === parts.length - 1) { if (/^\d+$/.test(part)) { if (!Array.isArray(current)) current = []; current[parseInt(part, 10)] = value; } else { current[part] = value; } } else { const nextPartIsNumeric = /^\d+$/.test(parts[i + 1]); if (!current[part]) { current[part] = nextPartIsNumeric ? [] : {}; } if (typeof current[part] !== "object" || current[part] === null) { throw new Error(`Invalid structure at key part: ${part}`); } current = current[part]; } } }); return result; } /** * Creates a new object containing only the specified keys from the original object. * * @template T The type of the object from which keys will be picked. * @template K The type of the keys to pick from the object. * * @param {PickFromObjectParams<T, K>} params - An object containing the original object and the keys to pick. * @param {T | undefined} params.obj - The original object from which keys will be picked. * @param {K[]} params.keys - An array of keys to pick from the object. * @returns {Pick<T, K>} A new object containing only the picked keys. */ function pickFromObject({ obj, keys }) { if (!obj) return {}; const result = {}; keys.forEach((key) => { if (key in obj) { result[key] = obj[key]; } }); return result; } /** * Creates a new object excluding the specified keys from the original object. * * @template T The type of the object from which keys will be omitted. * @template K The type of the keys to omit from the object. * * @param {OmitFromObjectParams<T, K>} params - An object containing the original object and the keys to omit. * @param {T | undefined} params.obj - The original object from which keys will be omitted. * @param {K[]} params.keys - An array of keys to omit from the object. * @returns {Omit<T, K>} A new object excluding the omitted keys. */ function omitFromObject({ obj, keys }) { if (!obj) return {}; const result = { ...obj }; keys.forEach((key) => { if (key in result) { delete result[key]; } }); return result; } /** * Creates a new object by prepending a specified string to each key of the original object. * * @param {object} obj - The original object whose keys will be modified. * @param {string} key - The string to prepend to each key. * @returns {object} A new object with modified keys. * * @example * const original = { name: 'Alice', age: 30 }; * const prepended = prependToObjectKey(original, 'user_'); * // Result: { user_name: 'Alice', user_age: 30 } */ function prependToObjectKey(obj, key) { if (!obj) { return {}; // Return an empty object if obj is null or undefined } const result = {}; Object.keys(obj).forEach((item) => { const newKey = `${key}${item}`; result[newKey] = obj[item]; }); return result; } /** * A TypeScript utility function to perform a deep merge of two objects, allowing for selective merging of nested properties. * * @param {object} target - The original object to merge into. * @param {object} source - The object containing updates. Only properties in `source` will overwrite target properties. * @returns {object} An object of type `T` with `target` properties overwritten by `source` properties where applicable. * * @example * const original = { * user: { * name: 'John', * address: { * city: 'New York', * zip: '10001' * } * } * }; * * const updates = { * user: { * address: { * city: 'San Francisco' * } * } * }; * * const merged = deepMerge({ target: original, source: updates }); * console.log(merged); * /* * { * user: { * name: 'John', * address: { * city: 'San Francisco', * zip: '10001' * } * } * } */ function deepMerge({ target, source, }) { const output = { ...target }; for (const key in source) { const targetValue = target[key]; const sourceValue = source[key]; if (isObject(targetValue) && isObject(sourceValue)) { // Recursively merge objects output[key] = deepMerge({ target: targetValue, source: sourceValue, }); } else if (sourceValue !== undefined) { output[key] = sourceValue; } } return output; } /** * Determines if the provided value is a non-null object. * * This function checks whether a given value is of type 'object' * and is not null. It is useful for type guarding to ensure that * a value is a valid object before performing operations on it. * * @param {unknown} value - The value to check. * @returns {boolean} - True if the value is a non-null object, false otherwise. */ function isObject(value) { return typeof value === "object" && value !== null; } /** * Converts the provided data to a JSON-compatible format by replacing * big integers with either numbers or strings and reviving date strings * to Date objects. * * @param {object | object[]} data - The data to serialize. * @returns {object | object[]} - The serialized data. * * @example * const data = { * id: 1234567890123456789, * name: "John Doe", * date: "2022-01-01T12:00:00.000Z", * }; * * const jsonified = jsonify(data); * // Result: { id: "1234567890123456789", name: "John Doe", date: Date object } */ function jsonify(data) { if (typeof data !== "object" || !Array.isArray(data)) return data; const replacer = (key, value) => { if (typeof value === "bigint") { return value <= Number.MAX_SAFE_INTEGER ? Number(value) : value.toString(); } return value; }; const reviver = (key, value) => { if (typeof value === "string" && (0, dateUtils_1.isValidDate)(value)) { return new Date(value); } return value; }; try { const serialized = JSON.stringify(data, replacer); return JSON.parse(serialized, reviver); } catch (error) { console.error("TransformData error:", error); return data; } }