javascript - 如何创建一个包含元组的集合,其中每个元组必须是唯一的?

标签 javascript typescript

我正在使用 TypeScript 并想创建一个对象集合。每个对象都有一些属性。属性的组合在集合中必须是唯一的。

所以这些样本组合是有效的

[
    [ 1, 2 ],
    [ 2, 1 ],
]

但添加另一个组合,例如[ 1, 2 ] 会抛出“key already exists”错误。

作为旁注:我的问题假设有 3 个键代表“复合键”。如果有更灵活的解决方案……为什么不呢。

我尝试在 JavaScript 中实现我自己的“类 map ”结构作为展示

class MyCollection {
  constructor() {
    this.items = [];
  }

  add(firstTupleItem, secondTupleItem, thirdTupleItem) {
    if (this.has(firstTupleItem, secondTupleItem, thirdTupleItem)) {
      console.log(`ERR: Combination of [${firstTupleItem}, ${secondTupleItem}, ${thirdTupleItem}] already exists!`);

      return;
    }

    console.log(`Added combination of [${firstTupleItem}, ${secondTupleItem}, ${thirdTupleItem}]`);

    this.items.push([firstTupleItem, secondTupleItem, thirdTupleItem]);
  }

  has(firstTupleItem, secondTupleItem, thirdTupleItem) {
    return this.items.some(item =>
      item[0] === firstTupleItem &&
      item[1] === secondTupleItem &&
      item[2] === thirdTupleItem);
  }
}

const myCollection = new MyCollection();

/* passes as expected */

myCollection.add(1, 2, 3);
myCollection.add(2, 1, 3);
myCollection.add(3, 1, 2);
myCollection.add(1, 3, 2);

/* fails as expected */

myCollection.add(1, 2, 3);

console.log(myCollection.items);

使用 map 可能会更快,但值(value)方面似乎被浪费了

class MyCustomMap extends Map<[number, number, number], [number, number, number]> {
    addItem(item: [number, number, number]) {
        super.set(item, item);
    }
}

我必须自己实现这样的集合还是有更好的解决方案? (使用 typescript )

最佳答案

你基本上想要一个 Set ,一个 JavaScript 集合,最多包含任何给定值中的一个;如果您 add()Set 的值与 Set 中已经存在的值相同,则不会发生任何变化。不幸的是,使两个值“相同”的定义并不是您想要的。 Set 和相关的 Map收藏使用"same-value zero" equality .对于像 stringnumber 这样的基元,这样的相等性很好,但是对于像 [1, 2] 这样的对象(是的,Arrays 是JS),它等于对象标识相等性,类似于您使用 === 得到的结果(区别仅在 NaN 附近):

const a = [1, 2];
const b = [1, 2];
console.log(a === b); // false

const c = a;
console.log(a === c); // true
const a = [1, 2];

这种做法是有道理的,尤其是考虑到可能的属性写入:

a[1] = 100;
console.log(a); // [1, 100]
console.log(b); // [1, 2]
console.log(c); // [1, 100]

但是由于您不打算保留数组引用并修改它们的内容(是吗?),您更愿意拥有类似于您自己的自定义相等函数的东西,其中如果两个数组的内容相等则它们相等。

不幸的是,SetMap do not support this directly .


如果您想要这样的东西,您需要自己实现。一种方法是提出一个函数 f() 将您的对象转换为原始键值,这样 f(o1) === f(o2) 当且仅当 o1o2 应被视为“相等”时。对基元数组执行此操作的最简单方法是使用 JSON.stringify() .

所以如果你的对象是 Props 类型:

type Props = [number, number, number];

那么转换函数f()可以写成propsToKey():

function propsToKey(props: Props): string {
    return JSON.stringify(props);
}

现在,在您的类(class)中,您持有这些键的 Set 而不是对象。或者,您可以保留一个 Map,该 Map 由值为对象的键作为键,这样您仍然可以根据需要返回原始对象。您将您关心的每个 Set 方法包装在适当调用 propsToKey() 的东西中。哦,因为您似乎希望您的 add() 方法采用可变数量的参数(例如,3 for [number, number, number])而不是数组,那么我们应该在适当的地方使用 rest/spread 语法。

好的,让我们这样做来实现MyCollection:

class MyCollection {
    private items: Map<string, Props> = new Map();
    add(...props: Props) {
        this.items.set(propsToKey(props), props);
        return this;
    }
    clear() {
        this.items.clear();
    }
    delete(...props: Props) {
        return this.items.delete(propsToKey(props));
    }
    forEach(cb: (...props: Props) => void) {
        return this.items.forEach(v => cb(...v));
    }
    has(...props: Props) {
        return this.items.has(propsToKey(props));
    }
    get size() {
        return this.items.size;
    }
    values() {
        return this.items.values();
    }
}

让我们测试一下:

const myCollection = new MyCollection();
myCollection.add(1, 2, 3);
myCollection.add(2, 1, 3);
myCollection.add(3, 1, 2);
myCollection.add(1, 3, 2);
console.log(Array.from(myCollection.values())) // [[1, 2, 3], [2, 1, 3], [3, 1, 2], [1, 3, 2]] 

myCollection.add(1, 2, 3);
console.log(Array.from(myCollection.values())) // [[1, 2, 3], [2, 1, 3], [3, 1, 2], [1, 3, 2]] 

看起来不错!

Playground link to code

关于javascript - 如何创建一个包含元组的集合,其中每个元组必须是唯一的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70005958/

相关文章:

javascript - 在 dom scrape 中寻找子元素的选择器

javascript - 如何在 Promises 上冒泡错误而不在每个级别调用 `catch`?

javascript - 在谷歌地图API中自定义自动完成字段

node.js - 单元测试 Nest JS Filter Catch 方法

javascript - 如何使用 Angular 获取典型的 json 数据

Angular getCurrentNavigation().extras

typescript - 从装饰器访问泛型类的静态成员

typescript 错误: jwt.解码不是函数

javascript - TextInput 光标重新获得焦点时会自动移动到前面

javascript - 无法访问index.js(Webpack)中的函数