繁体   English   中英

如何检查两个 Map 对象是否相等?

[英]How can I check if two Map objects are equal?

如何检查两个ES2015 Map对象是否具有相同的一组(key, value)对?

我们可以假设所有键和值都是原始数据类型。

解决此问题的一种方法是采用map.entries() ,从中创建数组,然后按键对该数组进行排序。 对另一个 map 做同样的事情。然后遍历这两个 arrays 来比较它们。 由于排序(性能低效)和使那些 arrays(内存效率低下),所有这些接缝都很麻烦而且效率也很低。

有人有更好的主意吗?

没有“标准”或“内置”的方式来做到这一点。 从概念上讲,您只需比较两个 Map 对象对于每个键具有相同的键和值,并且没有额外的键。

为了尽可能高效地进行比较,您可以进行以下优化:

  1. 首先检查两个地图上的.size属性。 如果两个映射没有相同数量的键,那么您马上就会知道,它们不能相同。
  2. 此外,保证它们具有相同数量的键允许您只迭代其中一个映射并将其值与另一个进行比较。
  3. 使用for (var [key, val] of map1)迭代器语法来迭代键,这样您就不必自己构建或排序键数组(应该更快,内存效率更高)。
  4. 然后,最后,如果您确保一旦发现不匹配就立即返回比较,那么当它们不相同时,它将缩短执行时间。

然后,由于undefined是 Map 中的合法值,但如果未找到键,它也是.get()返回的值,我们必须通过执行额外的.has()来注意我们正在比较的值是undefined

由于 Map 对象的键和值本身都可以是对象,因此如果您想要对对象进行深入的属性比较以确定相等性,而不是 Javascript 默认使用的更简单的===来测试同一对象,这会变得更加棘手. 或者,如果您只对具有键和值的基元的对象感兴趣,则可以避免这种复杂性。

对于仅测试严格值相等性的函数(检查对象以查看它们是否是相同的物理对象,而不是深度属性比较),您可以执行如下所示的操作。 这使用 ES6 语法来高效迭代地图对象,并在它们不匹配时尝试通过短路并在发现不匹配时立即返回false来提高性能。

 "use strict"; function compareMaps(map1, map2) { var testVal; if (map1.size !== map2.size) { return false; } for (var [key, val] of map1) { testVal = map2.get(key); // in cases of an undefined value, make sure the key // actually exists on the object so there are no false positives if (testVal !== val || (testVal === undefined && !map2.has(key))) { return false; } } return true; } // construct two maps that are initially identical var o = {"k" : 2} var m1 = new Map(); m1.set("obj", o); m1.set("str0", undefined); m1.set("str1", 1); m1.set("str2", 2); m1.set("str3", 3); var m2 = new Map(); m2.set("str0", undefined); m2.set("obj", o); m2.set("str1", 1); m2.set("str2", 2); m2.set("str3", 3); log(compareMaps(m1, m2)); // add an undefined key to m1 and a corresponding other key to m2 // this will pass the .size test and even pass the equality test, but not pass the // special test for undefined values m1.set("str-undefined", undefined); m2.set("str4", 4); log(compareMaps(m1, m2)); // remove one key from m1 so m2 has an extra key m1.delete("str-undefined"); log(compareMaps(m1, m2)); // add that same extra key to m1, but give it a different value m1.set("str4", 5); log(compareMaps(m1, m2)); function log(args) { var str = ""; for (var i = 0; i < arguments.length; i++) { if (typeof arguments[i] === "object") { str += JSON.stringify(arguments[i]); } else { str += arguments[i]; } } var div = document.createElement("div"); div.innerHTML = str; var target = log.id ? document.getElementById(log.id) : document.body; target.appendChild(div); }


如果您想进行深入的对象比较,而不仅仅是比较它们在物理上是否是同一个对象,其中值可以是对象或数组,那么生活就会变得更加复杂。

为此,您需要一种深度对象比较方法,该方法考虑了以下所有内容:

  1. 嵌套对象的递归比较
  2. 防止循环引用(可能导致无限循环)
  3. 了解如何比较某些类型的内置对象,例如Date

由于其他地方已经写了很多关于如何进行深度对象比较的文章(包括 StackOverflow 上的一些高票答案),我认为这不是您问题的主要部分。

如果您的Map只有字符串键,那么您可以使用这种方法来比较它们:

const mapToObj = (map) => {
  let obj = Object.create(null)
  for (let [k,v] of map) {
    // We don’t escape the key '__proto__'
    // which can cause problems on older engines
    obj[k] = v
  }
  return obj
}

assert.deepEqual(mapToObj(myMap), myExpectedObj)

注意: deepEqual是许多测试套件的一部分,如果没有,您可以使用 lodash/underscore 等效项。 任何进行深度比较的函数都可以。

mapToObj函数由http://exploringjs.com/es6/ch_maps-sets.html 提供

这是用于检查 map 相等性的单行代码 function:

const mapsAreEqual = (m1, m2) => m1.size === m2.size && Array.from(m1.keys()).every((key) => m1.get(key) === m2.get(key));

以上不适用于Map<string, object>因为在以下行中将无法正确评估两个对象:

if (testVal !== val || (testVal === undefined && !map2.has(key))) {

以下版本通过使用 JSON.stringify() 进行比较Map<string, object>扩展了Map<string, object>的功能

function compareMaps(map1, map2) {
    var testVal;
    if (map1.size !== map2.size) {
        return false;
    }
    for (var [key, val] of map1) {
        testVal = map2.get(key);
        // in cases of an undefined value, make sure the key
        // actually exists on the object so there are no false positives
        if (JSON.stringify(testVal) !== JSON.stringify(val) || (testVal === undefined && !map2.has(key))) {
            return false;
        }
    }
    return true;
}

您可以使用ConcurrentHashMap的 equals() 进行深度比较。

这并不能解决比较任何 Map 的问题,因为它是一个具体的实现,而不是 Map 接口。 但我相信它会为许多不需要保持抽象的人解决问题。

这是我的示例,能够提供可选的比较 function

/**
 * The utility function that returns an intersection of two Sets
 *
 * @returns an array of items in common
 */
function intersection<T>(a: Set<T>, b: Set<T>): T[] {
  return Array.from(a).filter(x => b.has(x));
}

/**
 * Compares two Maps
 *
 * @param compare is an optional function for values comparison
 * @returns `true` if they are equal, and `false` otherwise
 */
function compareMaps<T>(a: Map<string, T>, b: Map<string, T>, compare?: (aValue: T, bValue: T) => boolean): boolean {
  const common = intersection(new Set(a.keys()), new Set(b.keys()));
  return a.size === b.size && 
    common.length === a.size && 
    common.every(key => 
      compare?.(a.get(key) as T, b.get(key) as T) ?? a.get(key) === b.get(key));

}

请注意,@jfriend00 提出的解决方案在 typescript ES2016 中不起作用。 请参阅此以获取正确答案: iteration over a typescript map failed

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM