| 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 | */ |
| 8 | export 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 | */ |
| 26 | export 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 | */ |
| 44 | export 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 | }; |