![](/img/trans.png)
[英]How can i check if two arrays of objects are equal with javascript?
[英]How can I check if two Map objects are equal?
如何检查两个ES2015 Map对象是否具有相同的一组(key, value)
对?
我们可以假设所有键和值都是原始数据类型。
解决此问题的一种方法是采用map.entries()
,从中创建数组,然后按键对该数组进行排序。 对另一个 map 做同样的事情。然后遍历这两个 arrays 来比较它们。 由于排序(性能低效)和使那些 arrays(内存效率低下),所有这些接缝都很麻烦而且效率也很低。
有人有更好的主意吗?
没有“标准”或“内置”的方式来做到这一点。 从概念上讲,您只需比较两个 Map 对象对于每个键具有相同的键和值,并且没有额外的键。
为了尽可能高效地进行比较,您可以进行以下优化:
.size
属性。 如果两个映射没有相同数量的键,那么您马上就会知道,它们不能相同。for (var [key, val] of map1)
迭代器语法来迭代键,这样您就不必自己构建或排序键数组(应该更快,内存效率更高)。 然后,由于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); }
如果您想进行深入的对象比较,而不仅仅是比较它们在物理上是否是同一个对象,其中值可以是对象或数组,那么生活就会变得更加复杂。
为此,您需要一种深度对象比较方法,该方法考虑了以下所有内容:
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 等效项。 任何进行深度比较的函数都可以。
这是用于检查 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.