Skip to content

Commit

Permalink
fix(set): improve Set shallow copy performance and compatibility with…
Browse files Browse the repository at this point in the history
… new Set APIs (#69)
  • Loading branch information
unadlib authored Nov 22, 2024
1 parent 243b4ae commit 9cbea9f
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 9 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@
"tslib": "^2.7.0",
"typedoc": "^0.26.7",
"typedoc-plugin-markdown": "^4.2.8",
"typescript": "^5.6.2",
"typescript": "^5.6.3",
"yargs": "^17.7.2"
},
"config": {
Expand Down
31 changes: 31 additions & 0 deletions src/set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,4 +151,35 @@ export const setHandler = {
},
};

if (Set.prototype.difference) {
// for compatibility with new Set methods
// https://github.com/tc39/proposal-set-methods
Object.assign(setHandler, {
intersection(this: Set<any>, other: ReadonlySetLike<any>): Set<any> {
return Set.prototype.intersection.call(new Set(this.values()), other);
},
union(this: Set<any>, other: ReadonlySetLike<any>): Set<any> {
return Set.prototype.union.call(new Set(this.values()), other);
},
difference(this: Set<any>, other: ReadonlySetLike<any>): Set<any> {
return Set.prototype.difference.call(new Set(this.values()), other);
},
symmetricDifference(this: Set<any>, other: ReadonlySetLike<any>): Set<any> {
return Set.prototype.symmetricDifference.call(
new Set(this.values()),
other
);
},
isSubsetOf(this: Set<any>, other: ReadonlySetLike<any>): boolean {
return Set.prototype.isSubsetOf.call(new Set(this.values()), other);
},
isSupersetOf(this: Set<any>, other: ReadonlySetLike<any>): boolean {
return Set.prototype.isSupersetOf.call(new Set(this.values()), other);
},
isDisjointFrom(this: Set<any>, other: ReadonlySetLike<any>): boolean {
return Set.prototype.isDisjointFrom.call(new Set(this.values()), other);
},
});
}

export const setHandlerKeys = Reflect.ownKeys(setHandler);
4 changes: 3 additions & 1 deletion src/utils/copy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ export function shallowCopy(original: any, options?: Options<any, any>) {
if (Array.isArray(original)) {
return Array.prototype.concat.call(original);
} else if (original instanceof Set) {
return new Set(original.values());
return Set.prototype.difference
? Set.prototype.difference.call(original, new Set())
: new Set(original.values());
} else if (original instanceof Map) {
return new Map(original);
} else if (
Expand Down
4 changes: 2 additions & 2 deletions test/benchmark/results/result.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@
},
"set-batch": {
"name": "set-batch",
"avg": 2.501448439449168
"avg": 2.5015325732958087
},
"set": {
"name": "set",
"avg": 3.081842714116322
"avg": 3.9251446240188272
}
}
Binary file modified test/benchmark/results/set-batch.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified test/benchmark/results/set.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions test/immer-non-support.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,3 +617,28 @@ test('#61 - type issue: current of Draft<T> type should return T type', () => {
});
}
});

test('set - new Set API', () => {
// @ts-ignore
if (!Set.prototype.difference) {
console.warn('Set.prototype.difference is not supported');
return;
}
{
enableMapSet();
const odds = new Set([1, 3, 5, 7, 9]);
const squares = new Set([1, 4, 9]);
const state = produce(odds, (draft) => {
// @ts-ignore
expect(draft.intersection(squares)).toEqual(new Set([])); // it should be `new Set([1, 9])`
});
}
{
const odds = new Set([1, 3, 5, 7, 9]);
const squares = new Set([1, 4, 9]);
const state = create(odds, (draft) => {
// @ts-ignore
expect(draft.intersection(squares)).toEqual(new Set([1, 9]));
});
}
});
115 changes: 115 additions & 0 deletions test/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4103,3 +4103,118 @@ test('#61 - type issue: current of Draft<T> type should return T type', () => {
x: { y: new Set(['a', 'b']) },
});
});

describe('set - new API', () => {
// @ts-ignore
if (!Set.prototype.difference) {
return;
}
test('set - without Set.prototype.difference', () => {
// @ts-ignore
const difference = Set.prototype.difference;
// @ts-ignore
delete Set.prototype.difference;
const odds = new Set([{a: 1}]);
const state = create({ odds }, (draft) => {
// @ts-ignore
draft.odds.values().next().value.a = 2;
});
// @ts-ignore
Set.prototype.difference = difference;
});

test('set - Set.prototype.intersection', () => {
const odds = new Set([1, 3, 5, 7, 9]);
const squares = new Set([1, 4, 9]);
const state = create(odds, (draft) => {
// @ts-ignore
expect(draft.intersection(squares)).toEqual(new Set([1, 9]));
});
});

test('set - Set.prototype.union', () => {
const evens = new Set([2, 4, 6, 8]);
const squares = new Set([1, 4, 9]);
const state = create(evens, (draft) => {
// @ts-ignore
expect(draft.union(squares)).toEqual(new Set([2, 4, 6, 8, 1, 9]));
});
});

test('set - Set.prototype.difference', () => {
const odds = new Set([1, 3, 5, 7, 9]);
const squares = new Set([1, 4, 9]);
const state = create(odds, (draft) => {
// @ts-ignore
expect(draft.difference(squares)).toEqual(new Set([3, 5, 7]));
});
});

test('set - Set.prototype.symmetricDifference', () => {
const evens = new Set([2, 4, 6, 8]);
const squares = new Set([1, 4, 9]);
const state = create(evens, (draft) => {
// @ts-ignore
expect(draft.symmetricDifference(squares)).toEqual(
new Set([2, 6, 8, 1, 9])
);
});
});

test('set - Set.prototype.isSubsetOf', () => {
{
const fours = new Set([4, 8, 12, 16]);
const evens = new Set([2, 4, 6, 8, 10, 12, 14, 16, 18]);
const state = create(fours, (draft) => {
// @ts-ignore
expect(draft.isSubsetOf(evens)).toBe(true);
});
}
{
const primes = new Set([2, 3, 5, 7, 11, 13, 17, 19]);
const odds = new Set([3, 5, 7, 9, 11, 13, 15, 17, 19]);
const state = create(primes, (draft) => {
// @ts-ignore
expect(draft.isSubsetOf(odds)).toBe(false);
});
}
});

test('set - Set.prototype.isSupersetOf', () => {
{
const evens = new Set([2, 4, 6, 8, 10, 12, 14, 16, 18]);
const fours = new Set([4, 8, 12, 16]);
const state = create(evens, (draft) => {
// @ts-ignore
expect(draft.isSupersetOf(fours)).toBe(true);
});
}
{
const primes = new Set([2, 3, 5, 7, 11, 13, 17, 19]);
const odds = new Set([3, 5, 7, 9, 11, 13, 15, 17, 19]);
const state = create(odds, (draft) => {
// @ts-ignore
expect(draft.isSupersetOf(primes)).toBe(false);
});
}
});

test('set - Set.prototype.isDisjointFrom', () => {
{
const primes = new Set([2, 3, 5, 7, 11, 13, 17, 19]);
const squares = new Set([1, 4, 9, 16]);
const state = create(primes, (draft) => {
// @ts-ignore
expect(draft.isDisjointFrom(squares)).toBe(true);
});
}
{
const composites = new Set([4, 6, 8, 9, 10, 12, 14, 15, 16, 18]);
const squares = new Set([1, 4, 9, 16]);
const state = create(composites, (draft) => {
// @ts-ignore
expect(draft.isDisjointFrom(squares)).toBe(false);
});
}
});
});
Binary file modified test/performance/new-set-api.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion test/performance/new-set-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ const run = (size: number) => {
.add(
'Set - shallow copy with difference()',
() => {
const state = baseState.difference(new Set());
const state = Set.prototype.difference.call(baseState, new Set())
},
{
onStart: () => {
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6834,10 +6834,10 @@ typedoc@^0.26.7:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.2.tgz#891e1a90c5189d8506af64b9ef929fca99ba1ee5"
integrity sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==

typescript@^5.6.2:
version "5.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.2.tgz#d1de67b6bef77c41823f822df8f0b3bcff60a5a0"
integrity sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==
typescript@^5.6.3:
version "5.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.3.tgz#5f3449e31c9d94febb17de03cc081dd56d81db5b"
integrity sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==

uc.micro@^1.0.1, uc.micro@^1.0.5:
version "1.0.6"
Expand Down

0 comments on commit 9cbea9f

Please sign in to comment.