4

I would like to compare two objects and want to make one new object which have common proprty of it. I have seen lot of solutions to take difference but not sure how we can take common values.

Here is my objects where if obj1 have iscommisionAccount false and obj2 have iscommisonAccount true then my result should not have value as it is not matched if both have same property then only will get result.

I know we can use filter if we have arrays of objects but what if we have only objects.

obj 1 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":false,
      "istradeAccount":true
   }
}

 obj 2 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":true,
      "istradeAccount":true
   }
}

result = {
   "stream":{
      "istradeAccount":true
   }
}

diffrent examples:

obj 1 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":false,
      "istradeAccount":true
   }
}

 obj 2 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":true,
      "istradeAccount":false
   }
}

result = {
   "stream":{
   }
}

or

obj 1 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":true,
      "istradeAccount":true
   }
}

 obj 2 = {
   "val1":"test",
   "stream":{
      "iscommisonAccount":true,
      "istradeAccount":true
   }
}

result = {
   "stream":{
      "iscommisonAccount":true,
      "istradeAccount":true
   }
}
app
  • 197
  • 8
  • 24
  • 1
    I found solution form here [enter link description here](https://stackoverflow.com/questions/33232823/how-to-compare-two-objects-and-get-key-value-pairs-of-their-differences) – idersw Dec 08 '20 at 21:37
  • it seems getting difference then the common values. – app Dec 08 '20 at 21:41

2 Answers2

5

Something like this should work. A recursive function that just runs through all the keys of the object.

It doesn't handle arrays, but it can be modified to if that's a requirement.

function findCommonValues(obj1, obj2) {
    var result = {}
    for (let key in obj1) {
        if (obj1[key] && obj1[key] === obj2[key]) result[key] = obj1[key]
        else if (typeof obj1[key] === 'object' && obj1[key] !== null) {
            result[key] = findCommonValues(obj1[key], obj2[key])
        }
    }
    return result;
}

const obj1 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": false,
        "istradeAccount": true
    }
}

const obj2 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": true
    }
}

const obj3 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": false,
        "istradeAccount": true
    }
}

const obj4 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": false
    }
}

const obj5 = {
    "val1":"test",
    "stream":{
       "iscommisonAccount":true,
       "istradeAccount":true
    }
 }
 
 const obj6 = {
    "val1":"test",
    "stream":{
       "iscommisonAccount":true,
       "istradeAccount":true
    }
 }

console.log(findCommonValues(obj1, obj2))
console.log(findCommonValues(obj3, obj4))
console.log(findCommonValues(obj5, obj6))

If you want it as small as possible. This is really the best I can do.

const commonValues = (obj1, obj2) => Object.keys(obj1).reduce((result, key) => obj1[key] && obj1[key] === obj2[key] ? { ...result, [key]: obj1[key] } : typeof obj1[key] === 'object' && obj1[key] !== null ? { ...result, [key]: commonValues(obj1[key], obj2[key]) } : result, {});

const obj1 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": false,
        "istradeAccount": true
    }
}

const obj2 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": true
    }
}

const obj3 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": false,
        "istradeAccount": true
    }
}

const obj4 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": false
    }
}

const obj5 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": true
    }
}

const obj6 = {
    "val1": "test",
    "stream": {
        "iscommisonAccount": true,
        "istradeAccount": true
    }
}

console.log(commonValues(obj1, obj2))
console.log(commonValues(obj3, obj4))
console.log(commonValues(obj5, obj6))

TypeScript Version

export type KeyValueObject = {
    [key: string]: number | boolean | string | KeyValueObject
}

export const isKeyValueObject = (obj1: number | boolean | string | KeyValueObject): obj1 is KeyValueObject => typeof obj1 === 'object' && obj1 !== null;

export const commonValues = (obj1: KeyValueObject, obj2: KeyValueObject): KeyValueObject =>
    Object.keys(obj1).reduce((result, key) =>
        obj1[key] && obj1[key] === obj2[key]
            ? { ...result, [key]: obj1[key] }
            : isKeyValueObject(obj1[key]) && isKeyValueObject(obj2[key])
                ? { ...result, [key]: commonValues(obj1[key] as KeyValueObject, obj2[key] as KeyValueObject) }
                : result,
        {}
    );
Todd Skelton
  • 6,839
  • 3
  • 36
  • 48
  • thanks a lot.. it helps.. just as curiosity.. do we use any third party library like lodash or is there any typescript solution available to shorter the code? – app Dec 08 '20 at 21:44
  • 1
    I only see an intersect function for arrays, not objects in lodash. – Todd Skelton Dec 08 '20 at 21:53
  • 1
    I added another function that's about as compact as I can get it. It's one line but a long one line lol. – Todd Skelton Dec 08 '20 at 22:01
  • It is throwing some type error like this...Function implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. – app Dec 09 '20 at 15:41
  • 1
    You must be using `TypeScript`, I added a version for TypeScript that should work. – Todd Skelton Dec 09 '20 at 17:09
  • Thanks a lot.. Just one question.. how I can put this method in utilities and use it? – app Dec 09 '20 at 17:30
  • Just add that function to a `ts` file like `utilities.ts` and then use an import statement to use the function. `import { commonValues } from '../path/to/utilities'` – Todd Skelton Dec 09 '20 at 22:11
4

Here is a simple example that uses a combination of Object.values and Object.keys as arrays that are filtered to produce your prescribed output:

let obj1 = { "val1":"test", "stream":{ "iscommisonAccount":false, "istradeAccount":true } }; 
let obj2 = { "val1":"test", "stream":{ "iscommisonAccount":true, "istradeAccount":true } };


let obj3 = { "val1":"test", "stream":{ "iscommisonAccount":true, "istradeAccount":true } }; 
let obj4 = { "val1":"test", "stream":{ "iscommisonAccount":false, "istradeAccount":true } };

let obj5 = { "val1":"test", "stream":{ "iscommisonAccount":false, "istradeAccount":false } }; 
let obj6 = { "val1":"test", "stream":{ "iscommisonAccount":false, "istradeAccount":false } };

let obj7 = { "val1":"test", "stream":{ "iscommisonAccount":true, "istradeAccount":false } }; 
let obj8 = { "val1":"test", "stream":{ "iscommisonAccount":true, "istradeAccount":false } };

interface Data {stream:{[key: string]: boolean}};

function objFilter(objA: Data, objB: Data): Data {
  let out: Data = {stream:{}};
  Object.keys(objA.stream).filter((value, idx) =>
    Object.values(objA.stream)[idx] === Object.values(objB.stream)[idx] ? 
      out.stream[value] = Object.values(objA.stream)[idx] :
      false
  );
  return out;
}
console.log(objFilter(obj1, obj2)); //istradeAccount
console.log(objFilter(obj3, obj4)); //istradeAccount
console.log(objFilter(obj5, obj6)); //iscommisonAccount && istradeAccount
console.log(objFilter(obj7, obj8)); //iscommisonAccount && istradeAccount
console.log(objFilter(obj2, obj7)); //iscommisonAccount
Randy Casburn
  • 13,840
  • 1
  • 16
  • 31
  • it is failing if I do opposite, For example.. obj1 = { "val1":"test", "stream":{ "iscommisonAccount":true, "istradeAccount":true } } obj2 = { "val1":"test", "stream":{ "iscommisonAccount":false, "istradeAccount":true } } Result is coming: { "stream": { "iscommisonAccount": true, "istradeAccount": true } } – app Dec 09 '20 at 15:38
  • 1
    Yes, sorry, I naively was checking "backwards". The solution is now correct and shows various inputs. – Randy Casburn Dec 09 '20 at 16:18
  • Thanks a lot.. Just last question.. How I can cast this function properly.. because I get this 2 errors .. 1. at the out.stream[value] : (property) stream: {} Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'. No index signature with a parameter of type 'string' was found on type '{}'. – app Dec 09 '20 at 16:58
  • 2nd error: at function : (parameter) objA: any Parameter 'objA' implicitly has an 'any' type. – app Dec 09 '20 at 16:59
  • 3rd error : expected call-signature: 'objFilter' to have a typedef (typedef) – app Dec 09 '20 at 16:59
  • I am using tslint so it throws this – app Dec 09 '20 at 16:59
  • Now TypeScript aware. Please, in the future, if you want a TypeScript specific answer, provide TypeScript aware code in your question. I would hate to see the TypeScript errors cast by your original code! Don't mind providing the answer, it just streamlines the effort a bit. – Randy Casburn Dec 09 '20 at 19:24
  • Sure, Thanks a lot for your help – app Dec 09 '20 at 19:50
  • My pleasure - glad to help. Please consider accepting one of these answer that you find most helpful or that you end up using. – Randy Casburn Dec 09 '20 at 20:01