Last active
April 11, 2022 13:56
-
-
Save plukevdh/dec4b41d5b7d67f83be630afecee499e to your computer and use it in GitHub Desktop.
Ramda - Compact diff output for complex objects.
This file contains hidden or 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
import R from 'ramda' | |
const isObject = R.compose(R.equals('Object'), R.type); | |
const allAreObjects = R.compose(R.all(isObject), R.values); | |
const hasLeft = R.has('left'); | |
const hasRight = R.has('right'); | |
const hasBoth = R.both(hasLeft, hasRight); | |
const isEqual = R.both(hasBoth, R.compose(R.apply(R.equals), R.values)); | |
const markAdded = R.compose(R.append(undefined), R.values); | |
const markRemoved = R.compose(R.prepend(undefined), R.values); | |
const isAddition = R.both(hasLeft, R.complement(hasRight)); | |
const isRemoval = R.both(R.complement(hasLeft), hasRight); | |
const objectDiff = R.curry(_diff); | |
function _diff(l, r) { | |
return R.compose( | |
R.map(R.cond([ | |
[isAddition, markAdded], | |
[isRemoval, markRemoved], | |
[hasBoth, R.ifElse( | |
allAreObjects, | |
R.compose(R.apply(objectDiff), R.values), | |
R.values) | |
] | |
])), | |
R.reject(isEqual), | |
R.useWith(R.mergeWith(R.merge), [R.map(R.objOf('left')), R.map(R.objOf('right'))]) | |
)(l, r); | |
} | |
export default objectDiff; |
This file contains hidden or 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
import objectDiff from 'objectDiff' | |
const lhs = { | |
one: 1, | |
two: [1,2], | |
three: { more: 'items', over: 'here' }, | |
four: 'four', | |
five: 5, | |
}; | |
const rhs = { | |
one: 1, | |
two: [1,3], | |
three: { more: 'robots', over: 'here' }, | |
four: 4, | |
six: 6, | |
}; | |
describe("deep diffing", () => { | |
const diff = objectDiff(lhs, rhs); | |
it('only detects changed', () => { | |
expect(diff).to.have.all.keys('two', 'three', 'four', 'five', 'six'); | |
}); | |
it('diffs arrays by returning the full array of items', () => { | |
expect(diff.two).to.eql([[1,2], [1,3]]) | |
}); | |
it('diffs objects by returning only changed items', () => { | |
expect(diff.three).to.eql({more: ['items', 'robots']}); | |
}); | |
it('detects plain value differences', () => { | |
expect(diff.four).to.eql(['four', 4]) | |
}); | |
it('detects removed values', () => { | |
expect(diff.five).to.eql([5, undefined]) | |
}); | |
it('detects added values', () => { | |
expect(diff.six).to.eql([undefined, 6]) | |
}) | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@joeyfigaro I have something that does what you are describing which uses this gist. Mine actually takes a whitelist of fields that you want to ignore but if you don't pass a whitelist(spec object) it will just return True or False. I have attached below:
Also some test cases to see what the spec/whitelist object looks like:
It also supports more complex whitelist/spec objects.