1/**
2 * Shallow, order-sensitive array equality. `undefined`-tolerant: two
3 * `undefined`s are equal, an `undefined` and an array are not.
4 *
5 * Pass a custom `eq` comparator for non-primitive elements, e.g.
6 * `(x, y) => JSON.stringify(x) === JSON.stringify(y)`.
7 */
8export const arrayEquals = <T>(
9 a: ReadonlyArray<T> | undefined,
10 b: ReadonlyArray<T> | undefined,
11 eq: (x: T, y: T) => boolean = (x, y) => x === y,
12): boolean => {
13 if (a === b) return true;
14 if (a === undefined || b === undefined) return false;
15 if (a.length !== b.length) return false;
16 for (let i = 0; i < a.length; i++) {
17 if (!eq(a[i], b[i])) return false;
18 }
19 return true;
20};
21
22/**
23 * Order-insensitive array equality for primitive elements (compares
24 * sorted copies). `undefined`-tolerant like {@link arrayEquals}.
25 */
26export const arrayEqualsUnordered = <T extends string | number>(
27 a: ReadonlyArray<T> | undefined,
28 b: ReadonlyArray<T> | undefined,
29): boolean => {
30 if (a === b) return true;
31 if (a === undefined || b === undefined) return false;
32 if (a.length !== b.length) return false;
33 const as = [...a].sort();
34 const bs = [...b].sort();
35 for (let i = 0; i < as.length; i++) {
36 if (as[i] !== bs[i]) return false;
37 }
38 return true;
39};
40
41/**
42 * Shallow equality of two string records (same keys, same values).
43 */
44export const recordsEqual = (
45 a: Record<string, string>,
46 b: Record<string, string>,
47): boolean => {
48 const aKeys = Object.keys(a);
49 const bKeys = Object.keys(b);
50 if (aKeys.length !== bKeys.length) return false;
51 for (const k of aKeys) {
52 if (a[k] !== b[k]) return false;
53 }
54 return true;
55};