如何在JavaScript中断言部分深度相等/比较对象?

6
我有一个API,我想确保至少返回了期望的数据。如果返回更多数据,我不在意。
因此,我想比较两个对象(“expected”和“actual”),其中“expected”的所有属性必须等于“actual”,但“actual”可能包含更多属性:
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

一些更多的例子:
partiallyEqual({x: 1}, {a:2, x:1}) // return true
partiallyEqual({x: 1}, {a:2, x:2}) // return false (x is different)

如果actual包含额外的元素,数组可能(可选)会受到部分等效的影响。

partiallyEqual([1, 3], [1, 2, 3]) // return true
partiallyEqual([3, 1], [1, 2, 3]) // return false (different order)

1
你尝试过 Chai 的 deepEqual 吗? - Gyandeep
1
是的,但它是严格相等的,不会忽略任何属性。 - Michael_Scharf
由于您的用例与deepEqual有些不同,我建议您在这里查看:https://github.com/chaijs/deep-eql/blob/master/lib/eql.js 根据您的需求进行一些微调。 - Gyandeep
2
一个潜在的解决方案可能是创建 actual 的副本,在比较之前剥离所有不在 expected 中的属性。 - Michael_Scharf
1
对于一个小对象来说,这可能是正确的,但如果对象很大,那么这可能不是最佳解决方案。 - Gyandeep
显示剩余2条评论
2个回答

0

深度相等在不同情况下可能会很棘手,例如NaN !== NaN,还可能存在循环引用的情况。我最近编写了一个简单的深度相等检查工具函数,其中包括递归和循环引用检查,应该非常容易理解。您可以忽略一些长度检查以完成部分相等。

Github上的源代码:https://github.com/ryancat/simple-deep-equal


-1

我使用了一段时间前编写的递归函数:

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)

此函数将在发现旧对象与新对象之间存在差异时立即返回 false。如果新对象具有旧对象的所有属性,则新对象可以包含任何内容。


1
我使用一个名为prop的函数来实现这个功能:function prop(to, name, func) { Object.defineProperty(to.prototype, name, { value: func, writable: true, configurable: true }); return func; }它的用法如下:prop(Object, 'equals', function (to) {...})这些可以链式调用:prop(Object, 'equals', function (to) {prop(Array, 'equals', function (to) {...})})顺便说一下,扩展对象也会扩展数组,因为它扩展了所有东西。 - Akxe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接