perf(expect): add fast path for primitive keyed collections in equal() (#5934)

Co-authored-by: Asher Gomez <ashersaupingomez@gmail.com>
This commit is contained in:
lionel-rowe 2024-09-11 18:21:31 +08:00 committed by GitHub
parent 43102325b7
commit 38455481a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 56 additions and 2 deletions

View File

@ -5,8 +5,9 @@
import type { EqualOptions } from "./_types.ts";
import { AsymmetricMatcher } from "./_asymmetric_matchers.ts";
function isKeyedCollection(x: unknown): x is Set<unknown> {
return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>));
type KeyedCollection = Set<unknown> | Map<unknown, unknown>;
function isKeyedCollection(x: unknown): x is KeyedCollection {
return x instanceof Set || x instanceof Map;
}
function constructorsEqual(a: object, b: object) {
@ -153,6 +154,31 @@ export function equal(c: unknown, d: unknown, options?: EqualOptions): boolean {
return false;
}
const aKeys = [...a.keys()];
const primitiveKeysFastPath = aKeys.every((k) => {
return typeof k === "string" ||
typeof k === "number" ||
typeof k === "boolean" ||
typeof k === "bigint" ||
typeof k === "symbol" ||
k == null;
});
if (primitiveKeysFastPath) {
if (a instanceof Set) {
return a.symmetricDifference(b).size === 0;
}
for (const key of aKeys) {
if (
!b.has(key) ||
!compare(a.get(key), (b as Map<unknown, unknown>).get(key))
) {
return false;
}
}
return true;
}
let unmatchedEntries = a.size;
for (const [aKey, aValue] of a.entries()) {

View File

@ -262,3 +262,31 @@ Deno.test("equal() matches when values have circular references", () => {
mapB.set("prop", mapB);
assert(equal(mapA, mapB));
});
Deno.test("equal() fast path for primitive keyed collections", () => {
const arr = Array.from({ length: 10_000 }, (_, i) => i);
const set1 = new Set(arr);
const set2 = new Set(arr);
assert(equal(set1, set2));
const map1 = new Map(arr.map((v) => [v, v]));
const map2 = new Map(arr.map((v) => [v, v]));
assert(equal(map1, map2));
const set3 = new Set(arr);
const set4 = new Set(arr.with(-1, -1));
assertFalse(equal(set3, set4));
// entries [...] 9998 => 9998, 9999 => 9999
const map3 = new Map(arr.map((v) => [v, v]));
// entries [...] 9998 => 9998, -1 => -1
const map4 = new Map(arr.with(-1, -1).map((v) => [v, v]));
assertFalse(equal(map3, map4));
// entries [...] 9998 => 9998, 9999 => 9999
const map5 = new Map(arr.map((v, i) => [i, v]));
// entries [...] 9998 => 9998, 9999 => -1
const map6 = new Map(arr.with(-1, -1).map((v, i) => [i, v]));
assertFalse(equal(map5, map6));
});