vlens
Version:
Data Centric Routing & Rendering Mini-Framework
106 lines (100 loc) • 3.13 kB
text/typescript
/**
* Clone a value deeply if it's an object or array.
* @param value the value to be cloned
* @param key optional - indicates the key where this value was present in the parent object, if applicable
* @returns a clone of value
*/
export function deepCloneValue<T>(value: T, key?: string): T {
switch (typeof value) {
case 'object':
if (Array.isArray(value)) {
// @ts-ignore
return deepCloneArray(value);
} else if (value === null) {
// @ts-ignore
return null;
} else {
// @ts-ignore
return deepCloneObject(value);
}
case 'number':
case 'bigint':
case 'string':
case 'symbol':
case 'boolean':
return value
case 'function':
if (key) {
console.warn('deepCloneValue encountered a function value at', key)
} else {
console.warn('deepCloneValue encountered a function value!')
}
return value;
case 'undefined':
return value;
}
throw "unreachable"
}
export function deepCloneArray(data: unknown[]): typeof data {
return data.map(v => deepCloneValue(v))
}
export function deepCloneObject<T extends object>(data: T): T {
// @ts-ignore
let clone: T = {};
for (const [key, value] of Object.entries(data)) {
// @ts-ignore
clone[key] = deepCloneValue(value, key);
}
return clone;
}
export function isValueDifferent(value1: unknown, value2: unknown): boolean {
if (typeof value1 !== typeof value2) {
return true;
}
switch (typeof value1) {
case 'number':
case 'bigint':
case 'string':
case 'symbol':
case 'boolean':
return value1 !== value2;
case 'object':
if (Array.isArray(value1) && Array.isArray(value2)) {
return isArrayDifferent(value1, value2);
} else if (!Array.isArray(value1) && !Array.isArray(value2)) {
// @ts-ignore
return isObjectDifferent(value1, value2);
} else {
// one is array and one is object
return true;
}
case 'function':
return value1 !== value2;
case 'undefined':
return false;
}
}
export function isArrayDifferent(value1: unknown[], value2: unknown[]): boolean {
if (value1.length !== value2.length) {
return true;
}
for (let index = 0; index < value1.length; index++) {
if (isValueDifferent(value1[index], value2[index])) {
return true;
}
}
return false;
}
export function isObjectDifferent<T extends object>(data1: T, data2: T): boolean {
for (const [key, value1] of Object.entries(data1)) {
if (!(key in data2)) {
return true;
}
// @ts-ignore
const value2 = data2[key];
if (isValueDifferent(value1, value2)) {
return true;
}
}
return false;
}