13

Here is some example Javascript (ES6) code that does not do what one might intuitively imagine.

const exampleMap = new Map([[{a: 1}, 2]]);
console.log(exampleMap.get({a: 1}));

As it turns out, this prints undefined. Why? The reasoning is covered in this StackOverflow answer. Per the MDN entry for Map, Map uses === for key equality. And, per the MDN entry for ===, Objects are compared by reference equality.

That's all fine and good. It does exactly what the docs say it should. Unfortunately, what the above code is trying to do would be quite useful, even if it isn't the actual behavior per the spec.

How can I use Map, or what should I use instead of Map, to get a key-value lookup where the keys are compared by object deep-equality semantics?

Ming
  • 1,613
  • 12
  • 27
  • "object deep-equality semantics"? You mean stringification? Then stringify it, and a simple plain Object will do. `{a:1}` is semantically different than `{a:1}` in javascript. – Kaiido Jul 30 '19 at 01:31
  • See also [Define a custom hash() method for use with ES6 Maps](https://stackoverflow.com/q/31582852/1048572) – Bergi Jun 13 '21 at 23:35

3 Answers3

0

You could create your own function, where you pass through the map (m) you want to get the value of, and a key you want to search in the map. You can then stringify your key, such that you can search the keys in your map and compare them against this stringified key

const exampleMap = new Map([[{a: 1}, 2]]);

const getMapVal = (m, key) => {
  const strKey = JSON.stringify(key);
  return m.get(Array.from(m.keys()).find((k) => JSON.stringify(k) === strKey));
}

console.log(getMapVal(exampleMap, {a: 1})); // 2 (gives undefined if key doesn't exists)
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
0

Here's something that does a deep equality check. It relies on JSON.stringify though so it might be a performance issue for very large object keys. This also updates the Map prototype for use in the same way as .get.

Object.defineProperty(Map.prototype, 'deepCheck', {
  value: function(lookupKey) {
    let lookupValue;
    const lookupKeyStr = JSON.stringify(lookupKey);
    if (this == null) {
      throw new TypeError('this is null or not defined');
    }

    const iterator = this.entries();
    let result = null;


    while (true) {
      result = iterator.next();
      if (result.done) {
        break;
      }
      if (JSON.stringify(result.value[0]) === lookupKeyStr) {
        lookupValue = result.value[1];
      }
    }

    return lookupValue;
  }
});

const exampleMap = new Map([[{a: 1}, 2]]);

console.log(exampleMap.deepCheck({a: 1}));
GenericUser
  • 3,003
  • 1
  • 11
  • 17
-1

You need to somehow compare against or get a reference to the exact object that was used as the Map key. One option would be to iterate over the Map's entries, and check for one whose key has an a value of 1:

const exampleMap = new Map([[{a: 1}, 2]]);
const foundEntry = [...exampleMap.entries()]
  .find(([key, val]) => key.a === 1);
  
console.log(
  foundEntry
  ? foundEntry[1]
  : 'Not found!'
);

Another approach:

const exampleMap = new Map([[{a: 1}, 2]]);
const foundKey = [...exampleMap.keys()]
  .find(key => key.a === 1);
  
console.log(
  foundKey
  ? exampleMap.get(foundKey)
  : 'Not found!'
);
CertainPerformance
  • 356,069
  • 52
  • 309
  • 320