my-utils-kit
Version:
A lightweight and type-safe utility library for working with strings, objects, array Performance methods in TypeScript. Includes helpful methods for deep cloning, object transformations, safe access, query string handling, and more — designed for modern J
432 lines (338 loc) • 11.5 kB
text/typescript
export function deepCloneObj<T>(obj: T): T {
if (obj === null || typeof obj !== 'object') return obj;
if (Array.isArray(obj)) {
return obj.map(deepCloneObj) as unknown as T;
}
const clone: any = {};
for (const key in obj) {
clone[key] = deepCloneObj((obj as any)[key]);
}
return clone;
}
export function deepMerge<T extends Record<string, any>, U extends Record<string, any>>(target: T, source: U): T & U {
const result: any = { ...target };
for (const key in source) {
if (
source.hasOwnProperty(key) &&
typeof source[key] === 'object' &&
source[key] !== null &&
!Array.isArray(source[key])
) {
result[key] = deepMerge(result[key] || {}, source[key]);
} else {
result[key] = source[key];
}
}
return result;
}
export function isEmpty<T extends object>(obj: T): boolean {
if (Array.isArray(obj)) return false; // or throw error if not allowed
return Object.keys(obj).length === 0;
}
export function pick<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
const res = {} as Pick<T, K>;
for (let key of keys) {
res[key] = obj[key];
}
return res;
}
export function omit<T, K extends keyof T>(obj: T, keys: K[]): Omit<T, K> {
const res = { ...obj };
for (let key of keys) {
delete res[key];
}
return res;
}
export function flattenObject(obj: Record<string, any>, parentKey = '', result: Record<string, any> = {}): Record<string, any> {
for (const key in obj) {
if (!obj.hasOwnProperty(key)) continue;
const newKey = parentKey ? `${parentKey}.${key}` : key;
if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
flattenObject(obj[key], newKey, result);
} else {
result[newKey] = obj[key];
}
}
return result;
}
export function unflattenObject(obj: Record<string, any>): Record<string, any> {
const result: Record<string, any> = {};
for (const key in obj) {
const keys = key.split('.');
let current = result;
keys.forEach((k, index) => {
if (index === keys.length - 1) {
current[k] = obj[key];
} else {
if (!(k in current)) {
current[k] = {};
}
current = current[k];
}
});
}
return result;
}
export function invertObj<T extends Record<string, string | number | symbol>>(obj: T): Record<string, keyof T> {
const res: Record<string, keyof T> = {};
for (const key in obj) {
res[String(obj[key])] = key;
}
return res;
}
export function hasKey(obj: Record<string, any>, key: string): boolean {
return key in obj;
}
export function getNestedValue<T>(obj: Record<string, any>, path: string) {
return path.split('.').reduce((acc, key) => (acc && acc[key] !== undefined ? acc[key] : undefined), obj);
}
export function setNestedValue<T>(obj: Record<string, any>, path: string, value: T): boolean {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
// If the key doesn't exist or is not an object, create an empty object for it
if (!(key in current) || typeof current[key] !== 'object') {
current[key] = {};
}
current = current[key];
}
const lastKey = keys[keys.length - 1];
current[lastKey] = value;
return true;
}
export function deleteKey<T>(obj: Record<string, T>, key: string): Record<string, T> {
const newObj = { ...obj };
delete newObj[key];
return newObj;
}
export function deepEqual(obj1: any, obj2: any): boolean {
if (obj1 === obj2) return true;
if (typeof obj1 !== 'object' || typeof obj2 !== 'object' || obj1 === null || obj2 === null) {
return false;
}
const keys1 = Object.keys(obj1);
const keys2 = Object.keys(obj2);
if (keys1.length !== keys2.length) {
return false;
}
for (let key of keys1) {
if (!keys2.includes(key)) {
return false;
}
if (!deepEqual(obj1[key], obj2[key])) {
return false;
}
}
return true;
}
export function getDeepObjectSize(obj: Record<string, any>): number {
let size = 0;
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
size++;
if (typeof obj[key] === 'object' && obj[key] !== null) {
size += getDeepObjectSize(obj[key]);
}
}
}
return size;
}
export function transformObjectKeys<T>(obj: Record<string, T>, transformFn: (key: string) => string): Record<string, T> {
const result: Record<string, T> = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
const newKey = transformFn(key);
result[newKey] = obj[key];
}
}
return result;
}
export function transformObjectValues<T>(
obj: Record<string, T>,
transformFn: (value: T) => T
): Record<string, T> {
const result: Record<string, T> = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = transformFn(obj[key]); // Apply transform export function to the value
}
}
return result;
}
export function objectToQueryString(obj: Record<string, any>): string {
return new URLSearchParams(obj).toString();
}
export function queryStringToObject(queryString: string): Record<string, string> {
const params = new URLSearchParams(queryString);
const result: Record<string, string> = {};
const entries = (params as any).entries() as Iterable<[string, string]>;
for (const [key, value] of entries) {
result[key] = value;
}
return result;
}
export function deepFreeze(obj: Record<string, any>): void {
Object.freeze(obj);
Object.keys(obj).forEach(key => {
const value = obj[key];
if (value && typeof value === 'object') {
deepFreeze(value);
}
});
}
export function seal(obj: Record<string, any>): void {
Object.seal(obj);
}
export function deepSeal(obj: Record<string, any>): void {
Object.seal(obj);
Object.keys(obj).forEach(key => {
const value = obj[key];
if (value && typeof value === 'object') {
deepSeal(value);
}
});
}
export function deepIsFrozen(obj: Record<string, any>): boolean {
if (!Object.isFrozen(obj)) return false;
// Check for nested objects
return Object.keys(obj).every(key => {
const value = obj[key];
return !(value && typeof value === 'object') || deepIsFrozen(value);
});
}
export function deepIsSealed(obj: Record<string, any>): boolean {
if (!Object.isSealed(obj)) return false;
// Check for nested objects
return Object.keys(obj).every(key => {
const value = obj[key];
return !(value && typeof value === 'object') || deepIsSealed(value);
});
}
export function hasDeepKey(obj: Record<string, any>, key: string): boolean {
if (key in obj) return true;
for (const k in obj) {
if (obj.hasOwnProperty(k) && typeof obj[k] === 'object' && obj[k] !== null) {
if (hasDeepKey(obj[k], key)) {
return true;
}
}
}
return false;
}
export function isPlainObject(value: any): boolean {
return Object.prototype.toString.call(value) === '[object Object]';
}
export class TypeUtils {
static isNumber(value: any): boolean {
return typeof value === 'number';
}
static isString(value: any): boolean {
return typeof value === 'string';
}
static isBoolean(value: any): boolean {
return typeof value === 'boolean';
}
static isObject(value: any): boolean {
return value !== null && typeof value === 'object' && !Array.isArray(value);
}
static isArray(value: any): boolean {
return Array.isArray(value);
}
static isFunction(value: any): boolean {
return typeof value === 'function';
}
static isNull(value: any): boolean {
return value === null;
}
static isUndefined(value: any): boolean {
return typeof value === 'undefined';
}
static isSymbol(value: any): boolean {
return typeof value === 'symbol';
}
static isPlainObject(value: any): boolean {
return value !== null && typeof value === 'object' && Object.getPrototypeOf(value) === Object.prototype;
}
static isDate(value: any): boolean {
return Object.prototype.toString.call(value) === '[object Date]';
}
static isRegExp(value: any): boolean {
return Object.prototype.toString.call(value) === '[object RegExp]';
}
static isInstanceOf(value: any, constructor: Function): boolean {
return value instanceof constructor;
}
static getType(value: any): string {
return Object.prototype.toString.call(value).slice(8, -1); // Returns the internal class name (e.g., "Array", "Object", "Date")
}
}
export function isSubset<T>(obj1: Record<string, T>, obj2: Record<string, T>): boolean {
for (let key in obj1) {
if (!(key in obj2) || obj1[key] !== obj2[key]) {
return false;
}
}
return true;
}
export function groupByKeys<T>(arr: T[], keys: (keyof T)[]): Record<string, T[]> {
return arr.reduce((acc, obj) => {
const groupKey = keys.map(key => obj[key]).join('|'); // Use a separator
if (!acc[groupKey]) {
acc[groupKey] = [];
}
acc[groupKey].push(obj);
return acc;
}, {} as Record<string, T[]>);
}
export function arrayToObject<T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): Record<string, T> {
return arr.reduce((acc, obj) => {
const keyValue = String(obj[key]);
acc[keyValue] = obj;
return acc;
}, {} as Record<string, T>);
}
export function getUniqueByKey<T extends Record<string, any>, K extends keyof T>(arr: T[], key: K): T[] {
const seen = new Map<string, T>();
for (const item of arr) {
const keyValue = String(item[key]);
if (!seen.has(keyValue)) {
seen.set(keyValue, item);
}
}
return Array.from(seen.values());
}
export function shallowCopy<T extends object>(obj: T): T {
return { ...obj };
}
export function intersectObjects<T extends Record<string, any>>(obj1: T, obj2: T): Partial<T> {
const result: Partial<T> = {};
for (const key in obj1) {
if (obj2.hasOwnProperty(key) && obj1[key] === obj2[key]) {
result[key] = obj1[key];
}
}
return result;
}
export function diffObjects<T extends Record<string, any>>(obj1: T, obj2: T): Partial<T> {
const result: Partial<any> = {};
const allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);
for (const key of allKeys) {
if (obj1[key] !== obj2[key]) {
result[key] = obj1[key] !== undefined ? obj1[key] : obj2[key];
}
}
return result;
}
export function mapObjectValues<T, U>(
obj: Record<string, T>,
callback: (value: T, key: string, obj: Record<string, T>) => U
): Record<string, U> {
const result: Record<string, U> = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
result[key] = callback(obj[key], key, obj);
}
}
return result;
}