0

I have the following 2 objects in TypeScript and would like them to be considered as equal:

const obj = { 'id': 5, 'name': null };
const other = { 'id': 5 };

So the properties with 'null', 'undefined' values and not explicitly declared should be ignored. I tried Lodash _.isEqualWith but this seems not working (or I do not use it correctly):

customizer(objValue, othValue) {    
  if (!objValue && !othValue) {
    return true;
  }
  return undefined;
}
_.isEqualWith(obj, other, customizer);

Any suggestions would be appreciated.

apokryfos
  • 38,771
  • 9
  • 70
  • 114
SimonD
  • 1,441
  • 2
  • 18
  • 30
  • Creating a [copy of each object with null/undefined properties removed](https://stackoverflow.com/questions/286141/remove-blank-attributes-from-an-object-in-javascript) and comparing those would work, though there is probably a more efficient approach. – DBS Mar 17 '23 at 10:25

3 Answers3

0

Typescript does not interfere or extend the behavior of Javascript, it just provide and enforce typing. You can also cast one type to the other. So you need to code the function yourself.

Also, I think you need to provide more examples of objects that you consider equal so we can help you better, for now, I understand that two objects with at least one common key are equals for your case.


This function suffice the current example:

const obj = { 'id': 5, 'name': null };
const other = { 'id': 5 };

function compare(a, b){
    const keysToCompare = Object.keys({...a,...b}).filter(key => ![null,undefined].includes(a[key]) || ![null,undefined].includes(b[key]))

    return keysToCompare.every(key => a[key] === b[key])
}

console.log(compare(obj,other)) // true

Works with primitives, but it will fail when a key value is an object.

Riadh Adrani
  • 486
  • 2
  • 5
  • 16
  • I have objects with nested objects and those nested objects could have 'null' or 'undefined' values for primitive fields. I would like to exclude such fields from comparison. – SimonD Mar 17 '23 at 10:41
  • I updated with a function that suffice your example. – Riadh Adrani Mar 17 '23 at 10:56
0

To compare the two objects by ignoring null and undefined values, you can create a custom function that checks the equality of the two objects with your specific conditions.

Here my code

function compareObjectsIgnoringNullAndUndefined(obj1, obj2) {
  // Step 1: Check if both inputs are objects. If not, return false.
  if (typeof obj1 !== 'object') {
    return false;
  }

  if (typeof obj2 !== 'object') {
    return false;
  }

  // Step 2: Get filtered keys for both objects using the getFilteredKeys helper function.
  const filteredKeys1 = getFilteredKeys(obj1);
  const filteredKeys2 = getFilteredKeys(obj2);

  // Step 3: Check if the filtered key arrays have the same length using the hasSameLength helper function. If not, return false.
  if (!hasSameLength(filteredKeys1, filteredKeys2)) {
    return false;
  }

  // Step 4: Iterate through the filtered keys of the first object and check if the same key
  //         exists in the second object and if their values are equal using the every method. If not, return false.
  return filteredKeys1.every(key => filteredKeys2.includes(key) && obj1[key] === obj2[key]);
}

// Helper function to filter out keys with null or undefined values.
function getFilteredKeys(obj) {
  return Object.keys(obj).filter(key => obj[key] !== null && obj[key] !== undefined);
}

// Helper function to check if two arrays have the same length.
function hasSameLength(arr1, arr2) {
  return arr1.length === arr2.length;
}

const obj = { 'id': 5, 'name': null };
const other = { 'id': 5 };

console.log(compareObjectsIgnoringNullAndUndefined(obj, other)); 
// Output: true
0

One of the comparisons performed by _.isEqualWith() is that the same keys exist on both objects, since name doesn't exist on the 2nd object, the result is false.

If you explicitly add the name key (even if it's value is undefined the result would be true:

const obj = { id: 5, name: null };
const other = { id: 5, name: undefined }; // note the `name: undefined`

const customizer = (objValue, othValue) => 
  _.isNil(objValue) && _.isNil(othValue) ? true : undefined

const result = _.isEqualWith(obj, other, customizer);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

If you need to compare between objects with missing keys, use _.isMatchWith(), but note the the missing keys can only be on the 2nd object. Essentially, it's checking if the 2nd object is contained in the 1st.

const obj = { 'id': 5, 'name': null };
const other = { 'id': 5 };

const customizer = (objValue, othValue) => 
  _.isNil(objValue) && _.isNil(othValue) ? true : undefined

const result = _.isMatchWith(obj, other, customizer);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>

If you need symmetric comparison, you'll need to write a custom function that iterates the keys, compares them using your custom logic and plain equality, and deep compares them if they are nested objects:

const symmetricComp = predicate => (o1, o2) => 
  Object.keys({ ...o1, ...o2 }).every(key => { // iterate all combined keys
    const v1 = o1[key];
    const v2 = o2[key];
    
    if(predicate(v1, v2)) return true; // check using your custom logic
    
    if(v1 === v2) return true; // if they are actually equal
    
    if(_.isObject(v1) && _.isObject(v2)) // if objects iterate the properties with the same logic
      return symmetricComp(predicate)(v1, v2);
      
    return false;
  });

const compIgnoresNil = symmetricComp((v1, v2) => _.isNil(v1) && _.isNil(v2));

const obj = { id: 5, name: { first: 'a', last: null }, days: [1, 2, null] };
const other = { id: 5, name: { first: 'a' }, days: [1, 2], age: null };

const result = compIgnoresNil(obj, other);

console.log(result);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js" integrity="sha512-WFN04846sdKMIP5LKNphMaWzU7YpMyCU245etK3g/2ARYbPK9Ub18eG+ljU96qKRCWh+quCY7yefSmlkQw1ANQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
Ori Drori
  • 183,571
  • 29
  • 224
  • 209