I'm trying to compare two objects of the same type. What I want to achieve at the end, is a new object with only the properties that are different.
The solution implemented so far, with some previous help in another post, will, in fact, give me back an object with the properties that have changed, but, if the properties in inner objects are not in the same order, which happens since the ordering of object properties is non-standard in ECMAScript, I will also get those in the final object which is not what I want.
In the example below:
z
returns
{
"properties": {
"shared": false,
"query": "project!=\"JP\""
}
}
whilst I would want z
to be:
{
"properties": {
"query": "project!=\"JP\""
}
}
since the only property different between the two objects is query
type X = {
owner: {
accountId: string,
displayName: string
},
filter: {
id: string,
name: string,
},
properties: {
id: string,
query: string
shared: boolean
syncDate?: number
}
}
const a: X = {
filter: {
id: "10021",
name: "fil"
},
owner: {
accountId: "61498eeaa995ad0073bb8444",
displayName: "Jorge Guerreiro"
},
properties: {
id: "10021",
query: 'project!="JP"',
shared: false,
syncDate: undefined
}
}
const b: X = {
filter: {
id: "10021",
name: "fil"
},
owner: {
accountId: "61498eeaa995ad0073bb8444",
displayName: "Jorge Guerreiro"
},
properties: {
id: "10021",
shared: false,
query: 'project="JP"',
syncDate: undefined
}
}
const deepCompare = <T>(oldFilter: T, newFilter:T): Partial<T> => {
return Object.values(oldFilter).reduce((bef, aft, i) => {
const valueB = Object.values(newFilter)[i];
const keyB = Object.keys(newFilter)[i];
if (valueB instanceof Object && keyB) {
const delta = deepCompare(valueB, aft);
return Object.keys(delta).length > 0 ? { [keyB]: deepCompare(valueB, aft), ...bef } : bef;
}
return valueB !== aft && keyB ? { [keyB]: valueB, ...bef } : { ...bef };
}, {});
}
const z = deepCompare<X>(a, b)
I'm a bit stuck on how to have the recursive function below do what I want. Any help would be great.
Thanks
CodePudding user response:
try this
// scope to return the end result
const deepCompare = <T>(oldFilter: T, newFilter:T): Partial<T> => {
// resursive function where the target argument
// will be the filtered result of the current entry
const traverse = (obj: any, filter: any, target: any = {}) => {
// looping entries by key is the most performant way to
// iterate through obj properties
for (let k in obj)
if (obj[k] instanceof Object && filter[k]) {
// ad level, go deeper
target[k] = {}
let targetResult = traverse(obj[k], filter[k], target[k])
// delete empty entries if so desired
if (!Object.keys(targetResult).length)
delete target[k]
}
else if (obj[k] !== filter[k])
target[k] = obj[k] // store value
return target
}
return traverse(oldFilter, newFilter)
}
deepCompare(a,b)
for typing you need to make your objects indexable so that typescript doesnt complain, I usually do generic keyof typeof T[k]
or infer the nested agruments