/**
* Checks if an object is empty (has no enumerable properties).
* @param {Object} obj - The object to check.
* @returns {boolean | null } True if the object is empty, false otherwise, null if object is null or not an object
* @example
* isEmpty({}); // true
* isEmpty({ key: 'value' }); // false
*/
export function isEmpty(obj) {
if (obj === null || typeof obj !== 'object') {
console.error('isEmpty: Argument is not a valid object.');
return null;
}
return Object.keys(obj).length === 0;
}
/**
* Merges two objects, with properties from the second object overwriting those in the first.
* @param {Object} target - The target object to merge into.
* @param {Object} source - The source object to merge from.
* @returns { Object | null } The merged object, or null if one or both arguments are not valid objects.
* @example
* merge({ a: 1, b: 2 }, { b: 3, c: 4 }); // { a: 1, b: 3, c: 4 }
*/
export function merge(target, source) {
if (target === null || typeof target !== 'object' || source === null || typeof source !== 'object') {
console.error('merge: Both arguments must be valid objects.');
return null;
}
return { ...target, ...source };
}
/**
* Creates a deep clone of an object.
* @param {Object} obj - The object to clone.
* @returns { Object | null } The deep-cloned object or null if the argument is not a valid object
* @example
* const original = { a: 1, b: { c: 2 } };
* const clone = deepClone(original);
* clone.b.c = 3;
* console.log(original.b.c); // 2
*/
export function deepClone(obj) {
if (obj === null || typeof obj !== 'object') {
console.error('deepClone: Argument is not a valid object.');
return null;
}
return JSON.parse(JSON.stringify(obj));
}
/**
* Checks if an object has a specific property as its own (not inherited).
* @param {Object} obj - The object to check.
* @param {string} key - The property name to check for.
* @returns {boolean | null } True if the property exists, false otherwise, null if one or both arguments are not valid
* @example
* hasKey({ a: 1, b: 2 }, 'b'); // true
* hasKey({ a: 1, b: 2 }, 'c'); // false
*/
export function hasKey(obj, key) {
if (obj === null || typeof obj !== 'object') {
console.error('hasKey: First argument is not a valid object.');
return null;
}
if (typeof key !== 'string') {
console.error('hasKey: Second argument is not a valid string.');
return null;
}
return Object.prototype.hasOwnProperty.call(obj, key);
}
/**
* Retrieves the value at a given path within an object.
* @param {Object} obj - The object to query.
* @param {string} path - The path of the property to get.
* @param {*} [defaultValue] - The value returned if the path doesn't exist.
* @returns {*} The value at the specified path or the default value.
* @example
* const obj = { a: { b: { c: 3 } } };
* get(obj, 'a.b.c'); // 3
* get(obj, 'a.b.d', 'default'); // 'default'
*/
export function get(obj, path, defaultValue) {
if (obj === null || typeof obj !== 'object') {
console.error('get: First argument is not a valid object.');
return defaultValue;
}
if (typeof path !== 'string') {
console.error('get: Second argument is not a valid string.');
return defaultValue;
}
const pathArray = path.split('.');
let result = obj;
for (const key of pathArray) {
if (result[key] === undefined) {
return defaultValue;
}
result = result[key];
}
return result;
}
/**
* Sets the value at a given path within an object.
* @param {Object} obj - The object to modify.
* @param {string} path - The path of the property to set.
* @param {*} value - The value to set.
* @returns {Object} The updated object.
* @example
* const obj = { a: { b: { c: 3 } } };
* set(obj, 'a.b.c', 4);
* console.log(obj.a.b.c); // 4
* set(obj, 'a.b.d.e', 5);
* console.log(obj.a.b.d.e); // 5
*/
export function set(obj, path, value) {
if (obj === null || typeof obj !== 'object') {
console.error('set: First argument is not a valid object.');
return obj;
}
if (typeof path !== 'string') {
console.error('set: Second argument is not a valid string.');
return obj;
}
const pathArray = path.split('.');
let current = obj;
for (let i = 0; i < pathArray.length; i++) {
const key = pathArray[i];
if (i === pathArray.length - 1) {
current[key] = value;
} else {
if (current[key] === undefined || typeof current[key] !== 'object') {
current[key] = {};
}
current = current[key];
}
}
return obj;
}