|
| 1 | +import { is } from '../is'; |
| 2 | +import { NOT_SET } from '../TrieUtils'; |
| 3 | +import { isCollection } from '../predicates/isCollection'; |
| 4 | +import { isKeyed } from '../predicates/isKeyed'; |
| 5 | +import { isIndexed } from '../predicates/isIndexed'; |
| 6 | +import { isAssociative } from '../predicates/isAssociative'; |
| 7 | +import { isOrdered } from '../predicates/isOrdered'; |
| 8 | +import type { Collection } from '../../type-definitions/immutable'; |
| 9 | +import type { Repeat } from '../Repeat'; |
| 10 | +import type { Range } from '../Range'; |
| 11 | + |
| 12 | +export default function deepEqual( |
| 13 | + a: Range | Repeat | Collection<unknown, unknown>, |
| 14 | + b: unknown |
| 15 | +): boolean { |
| 16 | + if (a === b) { |
| 17 | + return true; |
| 18 | + } |
| 19 | + |
| 20 | + if ( |
| 21 | + !isCollection(b) || |
| 22 | + // @ts-expect-error size exists on Collection |
| 23 | + (a.size !== undefined && b.size !== undefined && a.size !== b.size) || |
| 24 | + // @ts-expect-error __hash exists on Collection |
| 25 | + (a.__hash !== undefined && |
| 26 | + // @ts-expect-error __hash exists on Collection |
| 27 | + b.__hash !== undefined && |
| 28 | + // @ts-expect-error __hash exists on Collection |
| 29 | + a.__hash !== b.__hash) || |
| 30 | + isKeyed(a) !== isKeyed(b) || |
| 31 | + isIndexed(a) !== isIndexed(b) || |
| 32 | + // @ts-expect-error Range extends Collection, which implements [Symbol.iterator], so it is valid |
| 33 | + isOrdered(a) !== isOrdered(b) |
| 34 | + ) { |
| 35 | + return false; |
| 36 | + } |
| 37 | + |
| 38 | + // @ts-expect-error size exists on Collection |
| 39 | + if (a.size === 0 && b.size === 0) { |
| 40 | + return true; |
| 41 | + } |
| 42 | + |
| 43 | + const notAssociative = !isAssociative(a); |
| 44 | + |
| 45 | + // @ts-expect-error Range extends Collection, which implements [Symbol.iterator], so it is valid |
| 46 | + if (isOrdered(a)) { |
| 47 | + const entries = a.entries(); |
| 48 | + // @ts-expect-error need to cast as boolean |
| 49 | + return ( |
| 50 | + b.every((v, k) => { |
| 51 | + const entry = entries.next().value; |
| 52 | + return entry && is(entry[1], v) && (notAssociative || is(entry[0], k)); |
| 53 | + }) && entries.next().done |
| 54 | + ); |
| 55 | + } |
| 56 | + |
| 57 | + let flipped = false; |
| 58 | + |
| 59 | + if (a.size === undefined) { |
| 60 | + // @ts-expect-error size exists on Collection |
| 61 | + if (b.size === undefined) { |
| 62 | + if (typeof a.cacheResult === 'function') { |
| 63 | + a.cacheResult(); |
| 64 | + } |
| 65 | + } else { |
| 66 | + flipped = true; |
| 67 | + const _ = a; |
| 68 | + a = b; |
| 69 | + b = _; |
| 70 | + } |
| 71 | + } |
| 72 | + |
| 73 | + let allEqual = true; |
| 74 | + const bSize: number = |
| 75 | + // @ts-expect-error b is Range | Repeat | Collection<unknown, unknown> as it may have been flipped, and __iterate is valid |
| 76 | + b.__iterate((v, k) => { |
| 77 | + if ( |
| 78 | + notAssociative |
| 79 | + ? // @ts-expect-error has exists on Collection |
| 80 | + !a.has(v) |
| 81 | + : flipped |
| 82 | + ? // @ts-expect-error type of `get` does not "catch" the version with `notSetValue` |
| 83 | + !is(v, a.get(k, NOT_SET)) |
| 84 | + : // @ts-expect-error type of `get` does not "catch" the version with `notSetValue` |
| 85 | + !is(a.get(k, NOT_SET), v) |
| 86 | + ) { |
| 87 | + allEqual = false; |
| 88 | + return false; |
| 89 | + } |
| 90 | + }); |
| 91 | + |
| 92 | + return ( |
| 93 | + allEqual && |
| 94 | + // @ts-expect-error size exists on Collection |
| 95 | + a.size === bSize |
| 96 | + ); |
| 97 | +} |
0 commit comments