Last active
December 23, 2021 16:54
-
-
Save brombal/6fc10953c0b082bb12444ba7feb24e4b to your computer and use it in GitHub Desktop.
getComparison - compare two objects using ===
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
Returns an object that can be used to compare `a` and `b` (and their nested properties) using `===`. | |
The return value will be structurally the same as `b`. However, any properties that are | |
equivalent objects (using a deep comparison) to `a`'s properties will be comparable using `===`. | |
This will be true for properties at any nested level, including the top level (i.e. if `a` and `b` | |
are equivalent, `a` will be identical to the return value). | |
E.g.: | |
``` | |
const a = { | |
firstName: 'Alex', | |
favoriteBook: { | |
title: 'Green Eggs and Ham', | |
author: 'Dr. Seuss' | |
}, | |
favoriteSong: { | |
title: 'Row row row your boat', | |
composer: 'Unknown' | |
} | |
}; | |
const b = { | |
firstName: 'Alex', | |
favoriteBook: { | |
title: 'Green Eggs and Ham', | |
author: 'Dr. Seuss' | |
}, | |
favoriteSong: { | |
title: 'Twinkle twinkle', | |
composer: 'Mozart' | |
} | |
}; | |
const comparison = getComparison(a, b); | |
a === comparison // false | |
a.firstName === comparison.firstName // true | |
a.favoriteBook === comparison.favoriteBook // true | |
a.favoriteSong === comparison.favoriteSong // false | |
``` | |
*/ | |
export function getComparison(a: any, b: any): any { | |
if (a === b) return a; | |
if (typeof a !== typeof b) return cloneDeep(b); | |
if (!b || !a) return cloneDeep(b); | |
if (typeof a === 'object') { | |
let changed = false; | |
const result: any = Array.isArray(a) ? [] : {}; | |
for (const key in b) { | |
const compared = getComparison(a[key], b[key]); | |
if (compared !== a[key]) { | |
result[key] = compared; | |
changed = true; | |
} else { | |
result[key] = a[key]; | |
} | |
} | |
for (const key in a) { | |
if (!(key in b)) changed = true; | |
} | |
return changed ? result : a; | |
} else { | |
return cloneDeep(b); | |
} | |
} | |
/** | |
* Deeply clones a value. | |
*/ | |
export function cloneDeep<T>(value: T): T { | |
if (!value || typeof value !== 'object') return value; | |
if (Array.isArray(value)) { | |
const clone: any = []; | |
for (let index = 0; index < value.length; ++index) { | |
clone.push(cloneDeep(value[index])); | |
} | |
return clone; | |
} | |
if (isPlainObject(value)) { | |
const clone: any = {}; | |
for (const key in value) { | |
clone[key] = cloneDeep(value[key]); | |
} | |
return clone; | |
} | |
return value; | |
} | |
/** | |
* Indicates that an object is most likely just an object literal. | |
*/ | |
export function isPlainObject(obj: any): boolean { | |
return obj && typeof obj === 'object' && obj.constructor.prototype === Object.prototype; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment