How to compare two objects but skip values and just compare keys and key types?

Viewed 38

I want to write unit testing using Jest for my Node.js functions and need to test if response object has some specific keys or not. Something like this:

expect(await mokChannelsService.getChannel(inputDto)).toEqualKeys(outputDto);

// Or just a normal function

const result = await mokChannelsService.getChannel(inputDto);
const equal = isEqualKeys(result, outputDto);

This toEqualKeys function should check equality just for keys not values. And these objects are not instances of a class. for example these two objects should be equal:

const object1 = {
  names: {
    firstName: '',
    lastName: '',
  },
  phone: 1,
};

const object2 = {
  names: {
    firstName: 'John',
    lastName: 'Due',
  },
  phone: 1234567890,
};

const object3 = {
  names: {
    firstName: 'John',
    lastName: 12,
  },
  mobile: '12345',
};

const result1 = toEqualKeys(object1, object2); // true
const result1 = toEqualKeys(object1, object3); // false

Is there any NPM packages available for this?

1 Answers

You can iterate each of the keys in object1, checking that the same key exists in object2 and - if the associated property in object1 is an object, that all the keys from that object also exist in a similar object in object2:

const object1 = {
  names: {
    firstName: '',
    lastName: '',
  },
  phone: 1,
};

const object2 = {
  names: {
    firstName: 'John',
    lastName: 'Due',
  },
  phone: 1234567890,
};

const object3 = {
  names: {
    firstName: 'John',
    lastName: 12,
  },
  mobile: '12345',
};

const toEqualKeys = (obj1, obj2) => Object.keys(obj1)
  .every(k => k in obj2 && 
    (typeof obj1[k] != 'object' || typeof obj2[k] == 'object' && toEqualKeys(obj1[k], obj2[k]))
  )
  
console.log(toEqualKeys(object1, object2))
console.log(toEqualKeys(object1, object3))

You can get more detail on the differences by logging differences into an array (using reduce to iterate the keys in the reference object). This function will return an array of errors (empty if there are none) for each comparison:

const object1 = {
  names: {
    firstName: '',
    lastName: '',
  },
  phone: 1,
};

const object2 = {
  names: {
    firstName: 'John',
    lastName: 'Due',
  },
  phone: 1234567890,
};

const object3 = {
  names: {
    firstName: 'John',
    lastName: 12,
  },
  mobile: '12345',
};

const object4 = {
  names: 'John',
  mobile: '12345',
};

const object5 = {
  names: {
    firstName: 'John',
    lastName: 12,
  },
  phone: { 
    landline : '12345',
    mobile : '555'
  }
};

const object6 = {
  names: {
    firtName: 'John',
    lastName: 'Due',
  },
  phone: 1234567890,
};


const notEqualKeys = (obj1, obj2, prefix = '') => Object.keys(obj1)
  .reduce((acc, k) => {
    if (!(k in obj2)) acc.push(`key ${k} not present in obj2${prefix}`)
    if (typeof obj1[k] == 'object') {
      if (typeof obj2[k] == 'object') {
        acc = acc.concat(notEqualKeys(obj1[k], obj2[k], `${prefix}.${k}`))
      }
      else {
        acc.push(`${k} is an object in obj1${prefix} but a value in obj2${prefix}`)
      }
    }
    else if (typeof obj2[k] == 'object') {
      acc.push(`${k} is a value in obj1${prefix} but an object in obj2${prefix}`)
    }
    return acc
  }, [])
  
console.log((arr = notEqualKeys(object1, object2)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object3)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object4)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object5)).length ? arr : true)
console.log((arr = notEqualKeys(object1, object6)).length ? arr : true)

Related