我正在使用 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 .对于像 string
和 number
这样的基元,这样的相等性很好,但是对于像 [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]
但是由于您不打算保留数组引用并修改它们的内容(是吗?),您更愿意拥有类似于您自己的自定义相等函数的东西,其中如果两个数组的内容相等则它们相等。
不幸的是,Set
和Map
do not support this directly .
如果您想要这样的东西,您需要自己实现。一种方法是提出一个函数 f()
将您的对象转换为原始键值,这样 f(o1) === f(o2)
当且仅当 o1
和 o2
应被视为“相等”时。对基元数组执行此操作的最简单方法是使用 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]]
看起来不错!
关于javascript - 如何创建一个包含元组的集合,其中每个元组必须是唯一的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70005958/