Created
September 3, 2023 00:36
-
-
Save westc/0b27f83cc767e4d83061f6a648be101e to your computer and use it in GitHub Desktop.
Determines the difference between 2 simple values.
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
function diff(left, right) { | |
function recurse(left, right, leftSeen, rightSeen, path) { | |
if (left === right || (left !== left && right !== right)) return []; | |
// If the constructors are different or if left and right are null and | |
// undefined but they are not the same... | |
if (left.constructor !== right?.constructor || left == null) { | |
return [{left, right, path, message: 'The values are not of the same type.'}]; | |
} | |
// If left and right are the same type of primitives... | |
let t = typeof left; | |
if (t !== 'object' && t !== 'function') { | |
return [{left, right, path, message: 'The values are not the same.'}]; | |
} | |
// Determine if left and/or right were seen before and if so return the | |
// appropriate diffs. | |
const leftSeenIndex = leftSeen.indexOf(left); | |
const rightSeenIndex = rightSeen.indexOf(right); | |
if (leftSeenIndex >= 0 || rightSeenIndex >= 0) { | |
if (leftSeenIndex === rightSeenIndex) return []; | |
return [{ | |
left, | |
right, | |
path, | |
message: leftSeenIndex < 0 | |
? "The right side is recursive but the left side isn't." | |
: rightSeenIndex < 0 | |
? "The left side is recursive but the right side isn't." | |
: "Both sides are recursive but refer to different values." | |
}]; | |
} | |
// At this point they must be of the same type so first see if they are iterable. | |
let areIterable = false; | |
try { | |
for (const _ of left) break; | |
areIterable = true; | |
} catch(e){} | |
const diffs = []; | |
if (areIterable) { | |
const leftLen = left.length, rightLen = right.length; | |
if (leftLen !== rightLen) { | |
diffs.push({ | |
left, | |
right, | |
path, | |
message: `The left side has a length of ${leftLen} while the right side has a length of ${rightLen}.` | |
}); | |
} | |
for (let i = 0, l = Math.min(leftLen, rightLen); i < l; i++) { | |
diffs.push(...recurse( | |
left[i], | |
right[i], | |
[...leftSeen, left], | |
[...rightSeen, right], | |
[...path, i] | |
)); | |
} | |
} | |
else { | |
for (const key of new Set(Object.keys(left).concat(Object.keys(right)))) { | |
if (Object.hasOwn(left, key)) { | |
if (Object.hasOwn(right, key)) { | |
diffs.push(...recurse( | |
left[key], | |
right[key], | |
[...leftSeen, left], | |
[...rightSeen, right], | |
[...path, key] | |
)); | |
} | |
else { | |
diffs.push({ | |
left, | |
right, | |
path, | |
message: `The left side has a ${JSON.stringify(key)} key but the right side doesn't.` | |
}); | |
} | |
} | |
else { | |
diffs.push({ | |
left, | |
right, | |
path, | |
message: `The right side has a ${JSON.stringify(key)} key but the left side doesn't.` | |
}); | |
} | |
} | |
} | |
return diffs; | |
} | |
return recurse(left, right, [], [], []); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment