6

I have an API and I want to assert that at least the expected data is returned. I don't care if more data is returned.

Therefore I want to compare two objects (expected and actual) where all attributes of expected must be equal to actual, but actual may contain more attributes:

var expected = {
    foo: 1,
    bar: {
        x1: 42,
        a1: [
            1,
            2,
            {
                x: 7
            }
        ]
    }
}

var actual = {
    foo: 1,
    whatever: 55, // to be ignored
    additional: { // to be ignored
        ignore: 1
    },
    bar: {
        x1: 42,
        a1: [
            1,
            2,
            {
                x: 7,
                y: 8   // to be ignored
            }
        ]
    }
}

partiallyEqual(expected, actual) // returns true

Some more examples:

partiallyEqual({x: 1}, {a:2, x:1}) // return true
partiallyEqual({x: 1}, {a:2, x:2}) // return false (x is different)

Arrays may (optionally) be subject to partial equivalent, if actual contains additional elements.

partiallyEqual([1, 3], [1, 2, 3]) // return true
partiallyEqual([3, 1], [1, 2, 3]) // return false (different order)
Michael_Scharf
  • 33,154
  • 22
  • 74
  • 95

2 Answers2

0

Deep equal can be tricky for varies cases, for example NaN !== NaN and there could be circular references. I wrote a simple deep equal check util function with recursive and circular reference check recently, and it should be pretty straight forward to follow. You can simply ignore some of the length check to have the partially equal done.

Source code on github: https://github.com/ryancat/simple-deep-equal

Xin Chen
  • 263
  • 2
  • 11
-1

I use this recursive function, that I wrote some time ago:

Object.prototype.equals = function(to) {
    for (var prop in to) {
        if (this.hasOwnProperty(prop) && to.hasOwnProperty(prop)) {
            if (to[prop] && typeof this[prop] == "object" && typeof to[prop] == "object") {
                if (!this[prop].equals(to[prop])) {
                    return false
                }
            } else if (this[prop] != to[prop]) {
                return false
            }
        }
    }
    return true;
};

({ x: { a: 1, b: 2 } }).equals({ x: { a: 1, b: 2 } }) => true
({ x: { a: 1, b: 2 } }).equals({ x: { a: 1, b: 1 } }) => false
({ x: [1,2] }).equals({ x: { 1:1,2:2 } }) => true (doesn't differentiate between array and/or object)

This function will return false as soon as it finds some difference in old vs new object. The new object can contain anything, if it have all the properties of the old object.

Akxe
  • 9,694
  • 3
  • 36
  • 71
  • Please see [How to define method in javascript on Array.prototype and Object.prototype so that it doesn't appear in for in loop](http://stackoverflow.com/a/13296897/1048572) and do your `Object.prototype` extension properly. – Bergi Aug 02 '15 at 13:42
  • 1
    I use function named prop that does it: function prop(to, name, func) { `Object.defineProperty(to.prototype, name, { value: func, writable: true, configurable: true }); return func; }` it is used as fallow `prop(Object, 'equals', function (to) {...})` these can be chained: `prop(Object, 'equals', function (to) {prop(Array, 'equals', function (to) {...})})` And by the way, extending object extends Array too, as it extends EVERYTHING – Akxe Aug 02 '15 at 20:24