In this blog post, we examine the ECMAScript proposal “Set methods for JavaScript” by Michał Wadas, Sathya Gunasekara and Kevin Gibbons. It introduces new methods for Sets.
Set.prototype.union(other) This method returns a Set that is the union of this and other.
Type signature:
Set<T>.prototype.union(other: SetReadOperations<T>): Set<T>
The type of other, SetReadOperations is discussed later. It means that other provides all operations that the new methods need for their algorithms.
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).union(new Set(['c', 'd'])),
new Set(['a', 'b', 'c', 'd'])
);
Set.prototype.intersection(other) This method returns a Set that is the intersection of this and other.
Type signature:
Set<T>.prototype.intersection(other: SetReadOperations<T>): Set<T>
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).intersection(new Set(['c', 'd'])),
new Set(['c'])
);
Set.prototype.difference(other) This method returns a Set that is the difference between this and other.
Type signature:
Set<T>.prototype.difference(other: SetReadOperations<T>): Set<T>
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).difference(new Set(['c', 'd'])),
new Set(['a', 'b'])
);
Set.prototype.symmetricDifference(other) This method returns a Set that is the symmetric difference between this and other. What does that mean? These are equivalent definitions of the symmetric difference:
this − other) ∪ (other − this)this ∪ other) − (this ∩ other)this xor other (exclusive OR)Type signature:
Set<T>.prototype.symmetricDifference(other: SetReadOperations<T>): Set<T>
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).symmetricDifference(new Set(['c', 'd'])),
new Set(['a', 'b', 'd'])
);
assert.deepEqual(
new Set(['a', 'b']).symmetricDifference(new Set(['c', 'd'])),
new Set(['a', 'c', 'b', 'd'])
);
Set.prototype.isSubsetOf(other) This method returns true if this is a subset of other and false otherwise.
Type signature:
Set<T>.prototype.isSubsetOf(other: SetReadOperations<T>): boolean
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).isSubsetOf(new Set(['a', 'b'])),
false
);
assert.deepEqual(
new Set(['a', 'b']).isSubsetOf(new Set(['a', 'b', 'c'])),
true
);
Set.prototype.isSupersetOf(other) This method returns true if this is a superset of other and false otherwise.
Type signature:
Set<T>.prototype.isSupersetOf(other: SetReadOperations<T>): boolean
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).isSupersetOf(new Set(['a', 'b'])),
true
);
assert.deepEqual(
new Set(['a', 'b']).isSupersetOf(new Set(['a', 'b', 'c'])),
false
);
Set.prototype.isDisjointFrom(other) This method returns true if this is disjoint from other and false otherwise.
Type signature:
Set<T>.prototype.isDisjointFrom(other: SetReadOperations<T>): boolean
Example:
assert.deepEqual(
new Set(['a', 'b', 'c']).isDisjointFrom(new Set(['c', 'd'])),
false
);
assert.deepEqual(
new Set(['a', 'b', 'c']).isDisjointFrom(new Set(['x'])),
true
);
this and other For all of the new Set methods:
this must be an instance of Set.other must implement the interface SetReadOperations shown below.
interface SetReadOperations<T> {
/** Can be `Infinity` (see next section). */
size: number;
has(key: T): boolean;
/** Returns an iterator for the elements in `this`. */
keys(): Iterator<T>; // only method `.next()` is required
}
The .size of other can be Infinity. That means we can work with infinite Sets:
const evenNumbers = {
has(elem) {
return (elem % 2) === 0;
},
size: Infinity,
keys() {
throw new TypeError();
}
};
assert.deepEqual(
new Set([0, 1, 2, 3]).difference(evenNumbers),
new Set([1, 3])
);
assert.deepEqual(
new Set([0, 1, 2, 3]).intersection(evenNumbers),
new Set([0, 2])
);
Only two methods don’t support other being an infinite Set:
unionsymmetricDifferenceThese are the rationales behind the API design (source):
Why does this have to be a Set?
this which would have enabled us to use the Set methods generically. However, not doing so makes implementations simpler and faster.Why use an interface for other?
other can be a data structure other than a Set. It was chosen as a compromise between accepting only Sets and any iterable objects.Why is the full interface always enforced for other?
Why was the method name .keys() chosen for iterating over data structure elements?
Map:
Symbol.iterator doesn’t work because that Map method returns key-value pairs.'values' doesn’t work because that Map method is not compatible with the Map method .has() (which accepts keys, not values).Why are the new method names nouns and not verbs like .add()?
this, while noun methods return new data – for example: set.add() and set.keys().I’m aware of the following two polyfills: