How can I check if two ES2015 Map objects have the same set of (key, value)
pairs?
We can assume that all the keys and values are primitive datatypes.
One approach to solve this would be to take the map.entries()
, create array from it, then sort that array by keys. And do the same thing with the other map. And then loop through those two arrays to compare them. All this seams cumbersome and also very inefficient because of sorting (performance inefficiency) and because of making those arrays (memory inefficiency).
Does anybody have better idea?
equals() method in Java is used to check for equality between two maps. It verifies whether the elements of one map passed as a parameter is equal to the elements of this map or not.
To check if all of the values in an object are equal, use the Object. values() method to get an array of the object's values and convert the array to a Set . If the Set contains a single element, then all of the values in the object are equal.
One approach to solve this would be to take the map. entries() , create array from it, then sort that array by keys. And do the same thing with the other map. And then loop through those two arrays to compare them.
There is no "standard" or "built-in" way to do this. Conceptually, you just have to compare that the two Map objects have the same keys and values for each key and have no extra keys.
To be as efficient about the comparison as possible, you can do the following optimizations:
.size
property on both maps. If the two maps don't have the same number of keys, then you know right away, they can't be identical. for (var [key, val] of map1)
iterator syntax for iterating the keys so you don't have to build or sort an array of keys yourself (should be both faster and more memory efficient).Then, since undefined
is a legal value in a Map, but it's also what .get()
returns if the key is not found, we have to watch out for that by doing an extra .has()
if the value we're comparing is undefined
.
Since both keys and values with a Map object can be objects themselves, this gets much trickier if you want a deep property comparison of objects to determine equality rather than just the more simple ===
that Javascript uses by default to test for the same object. Or, if you're only interested in objects that have primitives for keys and values, then this complexity can be avoided.
For a function that tests only strict value equality (checks objects to see if they are the same physical object, not a deep property comparison), you can do what is shown below. This uses ES6 syntax for efficient iteration of the map objects and attempts to improve performance when they do not match by short circuiting and returning false
as soon as a mismatch is found.
"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); }
If you wanted to do deep object comparison rather than just comparing to see if they are physically the same object, where values could be objects or arrays, then life gets a lot more complicated.
To do that, you need a deep object comparison method that takes into account all of the following:
Date
.Since a lot has been written elsewhere about how to do a deep object comparison (including a number of highly voted answers here on StackOverflow), I will assume that is not the main part of your question.
If your Map
has only string keys, then you can use this approach to compare them:
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)
Note: deepEqual
is part of many testing suites and if not, you can use lodash/underscore equivalents. Any function that does a deep comparison will do.
mapToObj
function courtesy of http://exploringjs.com/es6/ch_maps-sets.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With