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

brc7rcf0  于 2023-01-19  发布在  Java
关注(0)|答案(6)|浏览(1063)

如何检查两个ES2015 Map对象是否具有相同的(key, value)对集?
我们可以假设所有的键和值都是原始数据类型。
解决这个问题的一种方法是取map.entries(),从它创建数组,然后按键对数组排序。对另一个map做同样的事情。然后循环通过这两个数组来比较它们。所有这些看起来很麻烦,而且效率很低,因为排序(性能效率低)和创建这些数组(内存效率低)。
有人有更好的主意吗?

oalqel3c

oalqel3c1#

没有“标准”或“内置”的方法来实现这一点,从概念上讲,您只需要比较两个Map对象是否具有相同的键和每个键的值,并且没有额外的键。
为了尽可能高效地进行比较,可以执行以下优化:
1.首先检查两个Map的.size属性,如果两个Map的键数不同,那么你马上就知道它们不可能相同。
1.此外,保证它们具有相同数量的键允许您只迭代其中一个Map并将其值与另一个进行比较。
1.使用for (var [key, val] of map1)迭代器语法来迭代键,这样就不必自己构建键数组或对键数组进行排序(这样会更快,内存效率更高)。
1.最后,如果您确保比较在发现不匹配时立即返回,那么当它们不同时,它将缩短执行时间。
然后,由于undefined是Map中的法律的值,但它也是.get()在找不到键时返回的值,因此如果我们要比较的值是undefined,我们必须通过执行额外的.has()来注意这一点。
由于Map对象的键和值都可以是对象本身,所以如果你想对对象进行深入的属性比较来确定是否相等,而不是像Javascript默认使用的===那样测试同一个对象,那么这就变得非常复杂了,或者,如果你只对键和值的原语感兴趣,那么这种复杂性就可以避免。
对于一个只测试严格值相等的函数(检查对象是否是相同的物理对象,而不是深入的属性比较),您可以执行以下操作。它使用ES6语法来有效地迭代map对象,并尝试在它们不匹配时通过短路并在发现不匹配时立即返回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.嵌套对象的递归比较
1.防止循环引用(可能导致无限循环)
1.了解如何比较某些类型的内置对象,如Date
由于在其他地方已经有很多关于如何进行深度对象比较的文章(包括StackOverflow上的许多高投票率的答案),我将假设这不是您问题的主要部分。

4si2a6ki

4si2a6ki2#

如果您的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是许多测试套件的一部分,如果不是,你可以使用长划线/下划线的等价物。任何做深度比较的函数都可以。

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

e0bqpujr

e0bqpujr3#

下面是一个检查Map是否相等的单行函数:

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

zqry0prt4#

以上方法对Map<string, object>不起作用,因为下面一行将无法正确计算两个对象:
if (testVal !== val || (testVal === undefined && !map2.has(key))) {
以下版本通过使用JSON.stringify()进行比较来扩展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;
}
koaltpgm

koaltpgm5#

下面是我的示例,它能够提供一个可选的比较函数

/**
 * 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));

}

2nc8po8w

2nc8po8w6#

请注意@jfriend00提出的解决方案在类型脚本ES 2016中不起作用,请参阅以下内容以获得正确答案:在打印脚本Map上迭代失败

相关问题