Skip to content

Commit 2655270

Browse files
magicdawndai-shi
andauthored
fix(proxySet): fix .symmetricDifference,.isDisjointFrom; add .difference (#1040)
* fix(proxySet): fix `.symmetricDifference`,`.isDisjointFrom`; add `.difference` * chore(proxySet): re-order methods according to the proposal --------- Co-authored-by: Daishi Kato <[email protected]>
1 parent a940bc0 commit 2655270

File tree

3 files changed

+142
-31
lines changed

3 files changed

+142
-31
lines changed

docs/api/utils/proxySet.mdx

+4-3
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,12 @@ Native `Sets` store their data in internal slots which are not observable. This
1616
`proxySet` is useful when you need the functionality of a `Set` but still want to track changes to the data. `proxySet` can be useful if you're wanting to store unique values or if you want to perform mathematical `Set` operations on the data, such as union, intersection, or difference. `proxySet` supports all of the new methods introduced to `Set`:
1717

1818
- `intersection`
19-
- `isDisjointFrom`
19+
- `union`
20+
- `difference`
21+
- `symmetricDifference`
2022
- `isSubsetOf`
2123
- `isSupersetOf`
22-
- `symmetricDifference`
23-
- `union`
24+
- `isDisjointFrom`
2425

2526
You can see a full list of the methods supported by `proxySet` in the [MDN documentation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set).
2627

src/vanilla/utils/proxySet.ts

+42-28
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ type InternalProxySet<T> = Set<T> & {
1010
index: number
1111
epoch: number
1212
intersection: (other: Set<T>) => Set<T>
13-
isDisjointFrom: (other: Set<T>) => boolean
13+
union: (other: Set<T>) => Set<T>
14+
difference: (other: Set<T>) => Set<T>
15+
symmetricDifference: (other: Set<T>) => Set<T>
1416
isSubsetOf: (other: Set<T>) => boolean
1517
isSupersetOf: (other: Set<T>) => boolean
16-
symmetricDifference: (other: Set<T>) => Set<T>
17-
union: (other: Set<T>) => Set<T>
18+
isDisjointFrom: (other: Set<T>) => boolean
1819
}
1920

2021
/**
@@ -158,13 +159,44 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
158159
}
159160
return proxySet(resultSet)
160161
},
161-
isDisjointFrom(other: Set<T>): boolean {
162+
union(other: Set<T>) {
162163
this.epoch // touch property for tracking
164+
const resultSet = proxySet<T>()
163165
const otherSet = proxySet<T>(other)
164-
return (
165-
this.size === other.size &&
166-
[...this.values()].every((value) => !otherSet.has(value))
167-
)
166+
for (const value of this.values()) {
167+
resultSet.add(value)
168+
}
169+
for (const value of otherSet) {
170+
resultSet.add(value)
171+
}
172+
return proxySet(resultSet)
173+
},
174+
difference(other: Set<T>) {
175+
this.epoch // touch property for tracking
176+
const resultSet = proxySet<T>()
177+
const otherSet = proxySet<T>(other)
178+
for (const value of this.values()) {
179+
if (!otherSet.has(value)) {
180+
resultSet.add(value)
181+
}
182+
}
183+
return proxySet(resultSet)
184+
},
185+
symmetricDifference(other: Set<T>) {
186+
this.epoch // touch property for tracking
187+
const resultSet = proxySet<T>()
188+
const otherSet = proxySet<T>(other)
189+
for (const value of this.values()) {
190+
if (!otherSet.has(value)) {
191+
resultSet.add(value)
192+
}
193+
}
194+
for (const value of otherSet.values()) {
195+
if (!this.has(value)) {
196+
resultSet.add(value)
197+
}
198+
}
199+
return proxySet(resultSet)
168200
},
169201
isSubsetOf(other: Set<T>) {
170202
this.epoch // touch property for tracking
@@ -182,28 +214,10 @@ export function proxySet<T>(initialValues?: Iterable<T> | null) {
182214
[...otherSet].every((value) => this.has(value))
183215
)
184216
},
185-
symmetricDifference(other: Set<T>) {
186-
this.epoch // touch property for tracking
187-
const resultSet = proxySet<T>()
188-
const otherSet = proxySet<T>(other)
189-
for (const value of this.values()) {
190-
if (!otherSet.has(value)) {
191-
resultSet.add(value)
192-
}
193-
}
194-
return proxySet(resultSet)
195-
},
196-
union(other: Set<T>) {
217+
isDisjointFrom(other: Set<T>): boolean {
197218
this.epoch // touch property for tracking
198-
const resultSet = proxySet<T>()
199219
const otherSet = proxySet<T>(other)
200-
for (const value of this.values()) {
201-
resultSet.add(value)
202-
}
203-
for (const value of otherSet) {
204-
resultSet.add(value)
205-
}
206-
return proxySet(resultSet)
220+
return [...this.values()].every((value) => !otherSet.has(value))
207221
},
208222
}
209223

tests/proxySet.test.tsx

+96
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,99 @@ describe('ui updates - useSnapshot - iterator methods', () => {
847847
})
848848
})
849849
})
850+
851+
// https://github.com/tc39/proposal-set-methods
852+
describe('proposal set methods', () => {
853+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/intersection#examples
854+
it('.intersection', () => {
855+
const odds = proxySet([1, 3, 5, 7, 9])
856+
const squares = proxySet([1, 4, 9])
857+
const result = odds.intersection(squares) // Set(2) { 1, 9 }
858+
expect(result).toEqual(proxySet([1, 9]))
859+
})
860+
861+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/union#examples
862+
it('.union', () => {
863+
const evens = proxySet([2, 4, 6, 8])
864+
const squares = proxySet([1, 4, 9])
865+
const result = evens.union(squares) // Set(6) { 2, 4, 6, 8, 1, 9 }
866+
expect(result).toEqual(proxySet([2, 4, 6, 8, 1, 9]))
867+
})
868+
869+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/difference#examples
870+
it('.difference', () => {
871+
const odds = proxySet([1, 3, 5, 7, 9])
872+
const squares = proxySet([1, 4, 9])
873+
const result = odds.difference(squares) // Set(3) { 3, 5, 7 }
874+
expect(result).toEqual(proxySet([3, 5, 7]))
875+
})
876+
877+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/symmetricDifference#examples
878+
it('.symmetricDifference', () => {
879+
const evens = proxySet([2, 4, 6, 8])
880+
const squares = proxySet([1, 4, 9])
881+
const result = evens.symmetricDifference(squares) // Set(5) { 2, 6, 8, 1, 9 }
882+
expect(result).toEqual(proxySet([2, 6, 8, 1, 9]))
883+
})
884+
885+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSubsetOf#examples
886+
describe('.isSubsetOf', () => {
887+
it('The set of multiples of 4 (<20) is a subset of even numbers (<20)', () => {
888+
const fours = proxySet([4, 8, 12, 16])
889+
const evens = proxySet([2, 4, 6, 8, 10, 12, 14, 16, 18])
890+
expect(fours.isSubsetOf(evens)).toBe(true) // true
891+
})
892+
893+
it('The set of prime numbers (<20) is not a subset of all odd numbers (<20), because 2 is prime but not odd', () => {
894+
const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])
895+
const odds = proxySet([3, 5, 7, 9, 11, 13, 15, 17, 19])
896+
expect(primes.isSubsetOf(odds)).toBe(false) // false
897+
})
898+
899+
it('Equivalent sets are subsets of each other', () => {
900+
const set1 = proxySet([1, 2, 3])
901+
const set2 = proxySet([1, 2, 3])
902+
expect(set1.isSubsetOf(set2)).toBe(true) // true
903+
expect(set2.isSubsetOf(set1)).toBe(true) // true
904+
})
905+
})
906+
907+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isSupersetOf#examples
908+
describe('.isSupersetOf', () => {
909+
it('The set of even numbers (<20) is a superset of multiples of 4 (<20)', () => {
910+
const evens = proxySet([2, 4, 6, 8, 10, 12, 14, 16, 18])
911+
const fours = proxySet([4, 8, 12, 16])
912+
expect(evens.isSupersetOf(fours)).toBe(true) // true
913+
})
914+
915+
it('The set of all odd numbers (<20) is not a superset of prime numbers (<20), because 2 is prime but not odd', () => {
916+
const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])
917+
const odds = proxySet([3, 5, 7, 9, 11, 13, 15, 17, 19])
918+
expect(odds.isSupersetOf(primes)).toBe(false) // false
919+
})
920+
921+
it('Equivalent sets are supersets of each other', () => {
922+
const set1 = proxySet([1, 2, 3])
923+
const set2 = proxySet([1, 2, 3])
924+
expect(set1.isSupersetOf(set2)).toBe(true) // true
925+
expect(set2.isSupersetOf(set1)).toBe(true) // true
926+
})
927+
})
928+
929+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set/isDisjointFrom#examples
930+
describe('.isDisjointFrom', () => {
931+
it('The set of perfect squares (<20) is disjoint from the set of prime numbers (<20)', () => {
932+
// , because a perfect square is by definition decomposable into the product of two integers, while 1 is also not considered a prime number
933+
const primes = proxySet([2, 3, 5, 7, 11, 13, 17, 19])
934+
const squares = proxySet([1, 4, 9, 16])
935+
expect(primes.isDisjointFrom(squares)).toBe(true) // true
936+
})
937+
938+
it('The set of perfect squares (<20) is not disjoint from the set of composite numbers (<20)', () => {
939+
// , because all non-1 perfect squares are by definition composite numbers
940+
const composites = proxySet([4, 6, 8, 9, 10, 12, 14, 15, 16, 18])
941+
const squares = proxySet([1, 4, 9, 16])
942+
expect(composites.isDisjointFrom(squares)).toBe(false) // false
943+
})
944+
})
945+
})

0 commit comments

Comments
 (0)