Search code examples
javascripttypescriptobjectcomparison

Deep comparison of objects' properties of same type


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.

For example:

type A = {
    id: string;
    col: number;
    enabled: true;
    obj: {
        b: number;
    }
}

const a: A = {
    id: "ddd",
    col: 4,
    enabled: true,
    obj: {
        b: 4
    }
}

const b: A = {
    id: "dde",
    col: 4,
    enabled: true,
    obj: {
        b: 3
    }
}

const c = () => Object.values(a).reduce((bef, aft, i) => {
    const valuesB = Object.values(b);
    const keyB = Object.keys(b)[i];
    return valuesB[i] === aft ? { ...bef } : { keyB: valuesB[i], ...bef }
}, {})

With my logic above I was able to get a new object like:

{
  "keyB": "dde"
} 

It got the value right but the key wrong and also ignored the fact that the nested object property has different values. I'm a bit stuck and out of ideas.

Any help would be extremely appreciated.


Solution

  • About the incorrect key, you'd just have to add brackets around keyB to use the value of the variable as key: { [keyB]: valueB, ...bef }

    About the missing nested object, that's a bit more complicated but here's a solution:

    const diff = <T>(obj1: T, obj2: T): Partial<T> =>
        Object.values(obj1).reduce((bef, aft, i) => {
            const valueB = Object.values(obj2)[i];
            const keyB = Object.keys(obj2)[i];
            if (valueB instanceof Object) {
                const delta = diff(valueB, aft);
                return Object.keys(delta).length > 0 ? { [keyB]: diff(valueB, aft), ...bef } : bef;
            }
            return valueB === aft ? bef : { [keyB]: valueB, ...bef };
        }, {});
    
    const c = (): Partial<A> => diff(a, b);
    

    You have to check each field of the nested object separately. That's what the recursion is for.

    I also added typings to the functions. Partial<A> is a copy of A but makes all fields optional.

    The result is:

    {
        "obj": {
            "b": 4
        },
        "id": "dde"
    }