Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I check if two Map objects are equal?

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?

like image 284
Luka Avatar asked Mar 11 '16 19:03

Luka


People also ask

How do you know if two maps are equal?

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.

How do you know if values of objects are equal?

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.

Can we compare two maps in Javascript?

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.


2 Answers

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:

  1. First check the .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.
  2. Furthermore, guaranteeing that they have the same number of keys allows you to just iterate one of the maps and compare its values to the other.
  3. Use the 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).
  4. Then, lastly, if you make sure that the comparison returns immediately as soon as a mismatch is found, then it will shorten the execution time when they are not the same.

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:

  1. Recursive comparison for nested objects
  2. Protection against circular references (which can cause an infinite loop)
  3. Knowledge of how to compare some types of built-in objects such as a 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.

like image 71
jfriend00 Avatar answered Sep 29 '22 19:09

jfriend00


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

like image 44
Mrchief Avatar answered Sep 29 '22 20:09

Mrchief