1

How should lodash/underscore .isEqual(o1,o2) or assert.deepEqual(o1,o2) handle objects with undefined values? I'd like a deepEquals that returns true in a case like this:

 var left = {a: true, b: undefined};
 var right = {a: true};
 assert(_.isEqual(left, right))   // returns false

Alternatively, is there a way to test whether attribute b is defined as undefined versus simply not being defined?

 typeof left.b //  "undefined"
 typeof right.b // "undefined

 left.b === undefined  // true
 right.b === undefined // true

Right now i'm using this for expedience, but suspect it will yield false negatives:

var isEqual = function(o1, o2) { return JSON.stringify(o1) === JSON.stringify(o2)}

How to check for "undefined" in JavaScript?

Community
  • 1
  • 1
prototype
  • 7,249
  • 15
  • 60
  • 94

3 Answers3

3

Alternatively, is there a way to test whether attribute b is defined as undefined versus simply not being defined?

Yes, there are lots of ways to distinguish these cases.

"b" in left; // true
"b" in right; // false

left.hasOwnProperty("b"); // true
right.hasOwnProperty("b"); // false

left.propertyIsEnumerable('b'); // true
right.propertyIsEnumerable('b'); // false

Object.keys(left).includes("b"); // true
Object.keys(right).includes("b"); // false

Object.getOwnPropertyNames(left).includes("b"); // true
Object.getOwnPropertyNames(right).includes("b"); // false

Reflect.ownKeys(left).includes("b"); // true
Reflect.ownKeys(right).includes("b"); // false

!!Object.getOwnPropertyDescriptor(left, 'b'); // true
!!Object.getOwnPropertyDescriptor(right, 'b'); // false

!!Reflect.getOwnPropertyDescriptor(left, 'b'); // true
!!Reflect.getOwnPropertyDescriptor(right, 'b'); // false

!!(o => {for(var p in o) if(p==='b') return 1; return 0;})(left); // true
!!(o => {for(var p in o) if(p==='b') return 1; return 0;})(right); // false

Looking at the annotated source, I think this will be enough:

_.myEqual = function(a,b) {
  var keys = _.keys;
  _.keys = function(obj) { /* Hijack _.keys to filter out undefined properties */
    return _.filter(keys(obj), function(key){ return obj[key] !== void 0; });
  };
  var ret = _.isEqual(a,b); /* Call usual comparator, will use modified _.keys */
  _.keys = keys; /* Restore usual _.keys */
  return ret;
}

_.myEqual = function(a,b) {
  var keys = _.keys;
  _.keys = function(obj) {
    return _.filter(keys(obj), key => obj[key] !== void 0);
  };
  var ret = _.isEqual(a,b);
  _.keys = keys;
  return ret;
}
console.log(_.myEqual({a: true, b: undefined}, {a: true})); // true
console.log(_.myEqual({a: true, b: true}, {a: true})); // false
<script src="https://raw.githubusercontent.com/jashkenas/underscore/master/underscore.js"></script>
Oriol
  • 274,082
  • 63
  • 437
  • 513
2

this will strip out undefined and preserve the other desirable properties of assert.deepEqual and _.isEqual like insensitivity to attribute order:

assert.jsonEqual = function(a, b) { return deepEqual(JSON.parse(JSON.stringify(a)), JSON.parse(JSON.stringify(b))) }

prototype
  • 7,249
  • 15
  • 60
  • 94
0

The reason left is not equal to right is because b is a property on left, even though it's undefined.

To check to see if the property exists on an object, even if it's undefined (as in your example with left) use the in operator:

"b" in left; // true
"b" in right; // false

Javascript in operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/in


A quick function to check to see if a property exists and is undefined:

function propertyExistsAndIsUndefined(object, property) {
    return property in object && typeof object[property] === "undefined";
}
zeterain
  • 1,140
  • 1
  • 10
  • 15
  • Why not just `property in object && object[property] === undefined`? –  Jun 23 '16 at 04:50
  • `undefined` could be overwritten by someone else. Because `undefined` is just a property of the global object whose value is undefined, you could write `undefined = true` and now `object[property] === undefined` will return false. (Modern browsers do not allow `undefined` to be unwritable, but still.) `undefined` is also not a reserved word, so I suppose you could also make a local variable named `undefined`. The result of `typeof` should never change, making it more reliable. If it does, then there are probably larger problems on your hands. – zeterain Jun 23 '16 at 14:34
  • `object[property] === void 0` will also work because the `void` operator always evaluates to undefined. – zeterain Jun 23 '16 at 14:36
  • See http://stackoverflow.com/questions/8783510/how-dangerous-is-it-in-javascript-really-to-assume-undefined-is-not-overwritte. –  Jun 23 '16 at 14:49