Javascript 如何比较 Map 的键

问题描述 投票:0回答:4

我在 Node.js 应用程序中使用 Javascript ES6 功能:

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

在此代码中,我想使用一对

Int
作为我的地图的键 (
allElem
)。
问题是我不知道如何
Map
比较 javascript 中的对象。
有人可以帮助我吗?

javascript dictionary ecmascript-6
4个回答
12
投票

Map
确实使用 SameValueZero 算法 来比较密钥。这意味着引用相等用于对象,因此如果您有
a = new PairKey(1, 2)
b = new PairKey(1, 2)
,它们不是同一个对象 -
a !== b

那么你能做什么来解决这个问题呢?基本上有两种方法可以解决这个问题:

  • 不要使用对象本身作为键,而是使用它的原始(例如字符串)表示,它可以从具有相同值的不同实例创建
  • 对关键对象使用 hash consing,这样,如果使用相同的参数调用,
    new PairKey
    总是返回相同的对象

此外,您还可以对

Map
进行子类化,其中所有方法都被覆盖,以便它们依靠上述技术之一专门处理
PairKey

不幸的是,如果没有弱引用和内存泄漏,哈希consing是不可能实现的,所以我们不得不求助于第一种技术:

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

6
投票

您的代码失败的原因是 Map 使用同值算法来匹配键。一个对象实例与另一个对象实例的值不同,即使两者共享相同的内在值(例如,尝试

({a:1} === {a:1})
-> 它是 false)。一种适合您的方法是向对象添加一个键属性,以便相同的内在值生成完全相同的键(1 比 1)。然后在设置地图条目时使用该键。请参阅示例(利用
Symbol.for
生成可重现的密钥):

'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));


0
投票

我很抱歉提出这个老问题,但我发现了一个非常有趣的解决方案,灵感来自于Bergi的答案和提到的不可能的哈希consing

在下面的代码中,您还可以使用

Map
代替
Record
(对象)。

class PairKey {
  private static cache: Record<string, PairKey> = {};

  constructor(private _x: number, private _y: number) {
      const key = PairKey.key(_x, _y);

      if (PairKey.cache[key]) {
          return PairKey.cache[key];
      }

      PairKey.cache[key] = this;
  }

  private static key(x: number, y: number) {
      return `${x}_${y}`;
  }

  get x() {
      return this._x;
  }

  set x(x_pos: number) {
      this._x = x_pos;
  }

  get y() {
      return this._y;
  }

  set y(y_pos: number) {
      this._y = y_pos;
  }
}

const allElem = new Map<PairKey, string>();

allElem.set(new PairKey(1, 2), 'a');
allElem.set(new PairKey(2, 3), 'b');

console.log(allElem.has(new PairKey(1, 2))); // Returns true

-1
投票

您的地图键是一个对象。

console.log
返回
false
,因为您正在创建一个 新对象 来检索它们的密钥。拥有同一对并不重要。重要的是它是一个不同的新对象。

如果您想检索与

new pairKey(1,2)
对应的值,您必须执行以下操作:

let key = new pairKey(1,2);
allElem.set(key, 'a');

console.log(allElem.has(key));

换句话说,如果您使用对象作为键,请确保使用相同的对象来检索值。

© www.soinside.com 2019 - 2024. All rights reserved.