Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How Javascript compare key of Map

I am using Javascript ES6 features in a node.js application:

class pairKey {
constructor(x_pos, y_pos) {
    this._X = x_pos;
    this._Y = y_pos;
}

get x() {
    return this._X;
}
set x(x_pos) {
    this._X = x_pos;
}

get y() {
    return this._Y;
}
set y(y_pos) {
    this._Y = y_pos;
}


var allElem = new Map();
allElem.set(new pairKey(1,2), 'a');
allElem.set(new pairKey(2,3), 'b');

console.log(allElem.has(new pairKey(1,2))); //Should return true instead return false

In this code I want to use a pair of Int as key of my map (allElem).
The problem is that I don't know how Map compare objects in javascript.
Someone can help me?

like image 570
user72708 Avatar asked Aug 11 '15 19:08

user72708


People also ask

Are JavaScript Map keys unique?

Map objects are collections of key-value pairs. A key in the Map may only occur once; it is unique in the Map 's collection. A Map object is iterated by key-value pairs — a for...of loop returns a 2-member array of [key, value] for each iteration.

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.

What is key in Map JavaScript?

The Map. keys() method is used to extract the keys from a given map object and return the iterator object of keys. The keys are returned in the order they were inserted.

How do you compare object and Map JavaScript?

Few basic differences are as follows: In Object, the data-type of the key-field is restricted to integer, strings, and symbols. Whereas in Map, the key-field can be of any data-type (integer, an array, even an object!) In the Map, the original order of elements is preserved.


2 Answers

The reason your code fails is that Map uses same-value algorithm to match keys. An object instance is not the same value as another object instance, even if both share the same intrinsic value (for examle, try ({a:1} === {a:1}) -> it's false). One way you could make that work for you is to add a key property to your object such that the same intrinsic values generate the exact same key (1 to 1). Then use that key when setting Map entries. See example (Utilizes Symbol.for to generate a reproducable key):

'use strict'
class pairKey {
  constructor(x_pos, y_pos) {
    this._X = x_pos;
    this._Y = y_pos;
  }

  get x() {
    return this._X;
  }
  set x(x_pos) {
    this._X = x_pos;
  }

  get y() {
    return this._Y;
  }
  set y(y_pos) {
    this._Y = y_pos;
  }

  get key() {
    return Symbol.for(`pairKey[${this.x}:${this.y}]`);
  }
}
var allElem = new Map();
allElem.set(new pairKey(1, 2).key, 'a');
allElem.set(new pairKey(2, 3).key, 'b');

console.log(allElem.has(new pairKey(1, 2).key));
like image 119
Amit Avatar answered Oct 11 '22 10:10

Amit


Map does use the SameValueZero algorithm for comparing the keys. This means that reference equality is used for objects, so if you have a = new PairKey(1, 2) and b = new PairKey(1, 2) they are not the same object - a !== b.

So what can you do to solve this? There are basically two ways to solve this:

  • use not the object itself as a key, but rather a primitive (e.g. string) representation of it, which can be created from distinct instances with the same value
  • use hash consing for your key objects, so that new PairKey always returns the same object if called with the same arguments

Also you might be able to subclass Map where all methods are overwritten so that they handle PairKeys specially, relying on one of the above techniques.

Unfortunately, hash consing is impossible to implement without weak references and without leaking memory, so we'll have to resort to the first technique:

class Pair {
    constructor(x, y) {
        this.x = x;
        this.y = y;
    }
    toKey() {
        return `Pair(${this.x}, ${this.y})`;
    }
    static key(x, y) {
        return new Pair(x, y).toKey();
    }
}

var allElem = new Map(); // string -> string
allElem.set(Pair.key(1, 2), 'a');
allElem.set(Pair.key(2, 3), 'b');

console.log(allElem.has(Pair.key(1, 2))); // true
like image 21
Bergi Avatar answered Oct 11 '22 10:10

Bergi