Skip to content

Instantly share code, notes, and snippets.

@mukuljainx
Created December 5, 2020 13:52
Show Gist options
  • Save mukuljainx/3d3816cc6b6550a810be668396eb0f79 to your computer and use it in GitHub Desktop.
Save mukuljainx/3d3816cc6b6550a810be668396eb0f79 to your computer and use it in GitHub Desktop.
Returns the partial deep diff between two objects
type PartialDeep<T> = {
[P in keyof T]?: PartialDeep<T[P]>;
};
type objectType = Record<string, any>;
type nonObjectType = number | string | undefined | boolean | null;
const arrayDiff = <T extends nonObjectType | objectType>(
oldArr: T[],
newArr: T[],
_: string,
): Array<PartialDeep<T>> | undefined => {
const finalDiff: T[] = [];
newArr.forEach((item, index) => {
if (typeof oldArr[index] === 'object' && typeof item === 'object') {
const localDiff = objectDiff(
oldArr[index] as objectType,
item as objectType,
);
if (localDiff !== undefined) {
finalDiff.push(localDiff! as T);
}
} else if (typeof oldArr[index] !== typeof item) {
finalDiff.push(item);
} else {
const localDiff = diff(
oldArr[index] as nonObjectType,
item as nonObjectType,
);
if (localDiff !== undefined) {
finalDiff.push(localDiff! as T);
}
}
});
if (finalDiff.length === 0) {
return undefined;
}
return finalDiff as Array<PartialDeep<T>>;
};
export const diff = <T extends nonObjectType>(
oldVal: T,
newVal: T,
): T | undefined => (newVal !== oldVal ? newVal : undefined);
export const objectDiff = <T extends objectType>(
oldObj: T,
newObj: T,
arrayDiffFunc = arrayDiff,
): PartialDeep<T> | undefined => {
// basic checks
if (oldObj === null || newObj === null) {
return newObj;
}
if (typeof oldObj !== typeof newObj) {
return newObj;
}
if (typeof oldObj !== 'object') {
return oldObj !== newObj ? newObj : undefined;
}
const finalDiff: objectType = {};
const keySet = new Set([...Object.keys(oldObj!), ...Object.keys(newObj!)]);
const keys = Array.from(keySet);
keys.forEach(key => {
const newVal = newObj[key];
const oldVal = oldObj[key];
if (typeof newVal !== typeof oldVal) {
// if type is different value from new object
// will considered as changed values
finalDiff[key] = newVal;
} else {
const valType = typeof newVal;
switch (valType) {
case 'string':
case 'boolean':
case 'number':
case 'undefined': {
const localDiff = diff(oldVal, newVal);
if (localDiff !== undefined) {
finalDiff[key] = localDiff;
}
break;
}
case 'object': {
if (newVal === null || oldVal === null) {
if (newVal !== oldVal) {
finalDiff[key] = newVal;
}
} else if (Array.isArray(newVal) && Array.isArray(oldVal)) {
// both is array
const localDiff = arrayDiffFunc(oldVal, newVal, key);
if (localDiff !== undefined) {
finalDiff[key] = localDiff;
}
} else if (Array.isArray(newVal) || Array.isArray(oldVal)) {
// one is array
finalDiff[key] = newVal;
} else {
// both are plan object
const localDiff = objectDiff(oldVal, newVal);
if (localDiff !== undefined) {
finalDiff[key] = localDiff;
}
}
break;
}
default: {
break;
}
}
}
});
if (Object.keys(finalDiff).length === 0) {
return undefined;
}
return finalDiff as PartialDeep<T>;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment