【问题标题】:Object Comparing: check if an object contains the whole other object对象比较:检查一个对象是否包含整个其他对象
【发布时间】:2014-04-13 17:06:26
【问题描述】:

我有两个对象。它们的结构看起来有点像这样:

{
 education: ["school", "institute"],
 courses: ["HTML", "JS", "CSS"],
 Computer: {
        "OS":"WXP",
        "WS":"NotePad"
         }
 }

第二个:

{
 education: ["school", "university", "institute", "collage"],
 courses: ["HTML", "CSS", "JS", "Managing", "Directing"],
 Computer: {
        "OS":"WXP",
        "WS":"NotePad",
        "AV":"Avast"
         },
 something: function(){...},
 other: "thing"

}

您可能已经注意到,第二个对象包含整个第一个对象,以及第一个对象没有的一些项目。
我需要比较这两个对象,如果第二个对象包含第一个对象的每个项目,我需要得到答案(true-false)。
true - 如果第一个对象的所有项目也在第二个
false - 如果第一个对象的至少一个项目不在第二个中,例如:如果第二个对象没有“css”课程。

(第一个是要求,第二个是这个人有什么。我需要检查这个人是否具备所有要求)

可以是普通的 JS、jQuery 等。我不想为此使用服务器端语言。

有没有办法做到这一点?

谢谢!

【问题讨论】:

  • JSON.stringify(obj1.courses) == JSON.stringify(obj2.courses)
  • @adeneo 这不起作用——它检查对象是否相同,我们想检查一个是否是另一个的子集。事实上,理论上,如果它们相同,甚至不能保证返回true,因为对象中的字段可能以不同的顺序出现。而且它对数组中元素的顺序也很敏感;请注意,"courses" 数组在两种情况下都有不同顺序的元素。
  • @DavidKnipe - 是的,我知道,但它在某些情况下有效,如果顺序、大小写和一切都相等等,但这是比较对象的不好方法,比较对象在本身作为两个对象永远不会相同,应该检查某些属性或索引等。
  • @DavidKnipe 和 edeneo - 谢谢你们。但正如大卫之前所说 - 我不是要检查它们是否相等,而是要查看其中一个是否具有另一个对象的所有项目。

标签: javascript


【解决方案1】:

只需递归检查:

function isContainedIn(a, b) {
    if (typeof a != typeof b)
        return false;
    if (Array.isArray(a) && Array.isArray(b)) {
        // assuming same order at least
        for (var i=0, j=0, la=a.length, lb=b.length; i<la && j<lb;j++)
            if (isContainedIn(a[i], b[j]))
                i++;
        return i==la;
    } else if (Object(a) === a) {
        for (var p in a)
            if (!(p in b && isContainedIn(a[p], b[p])))
                return false;
        return true;
    } else
        return a === b;
}

> isContainedIn(requirements, person)
true

对于更类似于集合逻辑的数组方法,顺序无关紧要,添加类似

        a.sort();
        b = b.slice().sort()

(假设可排序的内容)在数组比较循环之前或用效率很低的替换它

        return a.every(function(ael) {
            return b.some(function(bel) {
                return isContainedIn(ael, bel);
            });
        });

【讨论】:

  • Bergi 和@BenjaminGruenbaum 我可以看到您的方向是正确的,但是即使此人没有所有要求,Bergi 的解决方案也会返回true,而@BenjaminGruenbaum 的解决方案是无穷无尽的。 .
  • 创造性地使用“对象”。我肯定在偷这个:)
  • @ReuvenKarasik:在循环引用的情况下,我的也会无限递归。不过,你几乎不会拥有这些。
  • 是的,我有一个,当我尝试使用 JSON.stringify 时出现错误,所以我修复了它。但你的也无济于事,因为它说true 即使b 没有对象a 中的所有
  • @ReuvenKarasik:你能做一个简单(简短)的例子吗?
【解决方案2】:

JavaScript(在 ES5 中)有两种复合原生类型(我假设您的代码中没有任何自定义集合,如果有的话 - 我假设它们支持“旧”迭代协议(具有 .length)

这是一个带注释的解决方案草图。我没有运行这个——它是为了让你了解如何实现这个算法。请注意,这会进入反向引用 (var a = {}; a.a =a}) 的无限循环。

function sub(big,small){
    if(typeof big === "function") return small === big; // function reference equality.
    if(big.length){ // iterable, for example array, nodelist etc. (even string!)
        if(small.length > big.length) return false; // small is bigger!
        for(var i = 0; i < small.length; i++ ){
            if(!sub(big[i],small[i])){ // doesn't have a property
                return false;
            }
        }
        return true; // all properties are subproperties recursively
    }
    if(typeof big === "object" && big !== null){
        // I assume null is not a subset of an object, you may change this, it's conceptual
        if(typeof small !== "object" || small === null) return false; 
        for(var key in small){
            // I consider the prototype a part of the object, you may filter this with a 
            // hasOwnProperty check here.
            if(!sub(big[key],small[key])){ // doesn't have a property
                 return false;
            }
            return true;
        }
    }
    return big === small; // primitive value type equality
                          // , or ES7 value type equality, future compat ftw :P
}

【讨论】:

  • 嘿,一个问题:你提到的无限循环在哪里,为什么它是无限的?谢谢!
  • 我玩了一下你的代码,我注意到if(!sub(big[key],small[key])) 行是使循环无限的那一行,我只是不太明白为什么。我的意思是,子函数是放置在网站本身,你为什么要这样做?
  • @ReuvenKarasik 它被称为递归,它是编程中一个非常重要且非常基础的概念。我通过在较小的条件下解决相同的问题来定义问题(在循环参考案例中,它永远不会变小)。让我们看看它有多强大:我希望您尽可能严格地定义一个对象作为另一个对象的子对象意味着什么(这些对象当然可以由其他对象组成)?
  • 嗯,这正是你所说的——你有两个对象,一个在另一个里面。您可以通过调用大的 - Obj1、Obj1.Obj2 来达到两者。你要去哪里问这个问题? ;)
  • @ReuvenKarasik 我会到达那里,别担心。定义“里面”,一个对象在另一个对象里面是什么意思?
【解决方案3】:

编辑:没有注意到merge 更改了第一个参数...更改了代码,但它仍然会导致 obj2 更改。您可以添加 _.cloneDeep(obj2) 来解决这个问题,但到那时我的解决方案似乎并不那么优雅。也用cloneDeep 更新了演示。

Edit2:由于JSON.stringify 要求您比较的对象中对象属性的顺序相同,因此您可以改用Object comparison in JavaScript 之类的东西。但是,在演示中您可以看到它有效,所以我想说,对于您的情况,使用 _.mergeJSON.stringify 很有可能是可靠的。

使用lo-dash,您可以使用_.merge 并检查结果是否与较大的对象相同。

function(obj1, obj2) {
    var obj3 =_.merge(_.cloneDeep(obj2), obj1);
    return JSON.stringify(obj3) === JSON.stringify(obj1);
}

demo

当然,另一种选择是使用 vanilla JS 遍历整个对象。

【讨论】:

  • 贝尔吉是对的。您可以通过调整演示来看到这一点。
  • 编辑以反映这一点,我的错
  • 这个字符串化的答案是不可靠的,因为对象中的元素可能在两个stringify 操作中以不同的顺序出现。我也不确定数组会发生什么。
  • @DavidKnipe 编辑了一个指向用于比较对象的最佳答案的链接。
  • 我测试了你的小提琴,即使我删除了第一个元素的“css”,它也会显示“true”。我了解您的解决方案,它很酷,但不符合我的需求。
【解决方案4】:

//当对象顺序不同时

function isContainedIn(a, b) {
    if (typeof a != typeof b)
        return false;
    if (Array.isArray(a) && Array.isArray(b)) {
        if(a.length == 1) {
            var j=0;
            while (j < b.length) {
                if ((isContainedIn( a[0], b[j]))) {
                    return true;
                }
                j++;
            }
            return false;
        } else {
            var k=0;
            while (k < a.length) {
                if (!(isContainedIn([a[k]], b))) {
                    return false;
                }
                k++;
            }
            return true;
        }
    } else if (Object(a) === a) {
        for (var p in a)
            if (!(p in b && isContainedIn(a[p], b[p])))
                return false;
        return true;
    } else
        return a === b;
};


isContainedIn(requirements, person)
true

【讨论】:

  • 你好,你能解释一下你在那里做什么吗?
【解决方案5】:

除了本杰明的回答 - 你可以测试一下:

const sub = (big, small) => {
    if (typeof big === 'function' || typeof small === 'string') return small === big; // function or string reference equality
    if (big && big.length) { // iterable, for example array, nodelist etc. (even string!)
        if (small.length > big.length) return false; // small is bigger!
        for (let i = 0; i < small.length; i++)
            if (!sub(big[i], small[i])) // doesn't have a property
                return false;
        return true; // all properties are subproperties recursively
    }
    if (typeof big === 'object' && big !== null) {
        // I assume null is not a subset of an object, you may change this, it's conceptual
        if (typeof small !== 'object' || small === null) return false;
        // console.log(Object.keys(small));
        for (const key of Object.keys(small)) {
            // I consider the prototype a part of the object, you may filter this with a
            // hasOwnProperty check here.
            if (sub(big[key], small[key]) === false) // doesn't have a property
                return false;
            continue;
        }
        return true;
    }
    return big === small; // primitive value type equality
};

甚至使用更清洁的解决方案: https://github.com/blackflux/object-deep-contain

【讨论】:

    猜你喜欢
    • 2022-11-09
    • 2019-05-20
    • 1970-01-01
    • 2020-11-29
    • 1970-01-01
    • 2020-06-07
    • 1970-01-01
    • 2021-03-09
    • 2017-08-25
    相关资源
    最近更新 更多