From c98e4621420814a388e8703dbde58cb4827d0f21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Herv=C3=A9=20BECHER?= Date: Sat, 24 May 2025 12:11:44 +0200 Subject: [PATCH] sync --- package-lock.json | 252 +++++++++++----------- package.json | 8 +- src/async/impl.ts | 314 +++++++++++++++------------ src/async/types.ts | 70 ++++--- src/collector/impl.ts | 86 ++++++-- src/collector/index.ts | 10 +- src/collector/v2/impl.ts | 219 +++++++++++++++++++ src/collector/v2/index.ts | 57 +++++ src/collector/v2/types.ts | 8 + src/comparer/async.ts | 245 ++++++++++++++++++++++ src/comparer/sync.ts | 244 +++++++++++++++++++++ src/comparer/types.ts | 35 ++++ src/equality-comparer/index.ts | 15 ++ src/equality-comparer/types.ts | 6 + src/equality-map.ts | 59 ++++-- src/equality-set.ts | 133 ++++++++---- src/index.ts | 11 +- src/sorting.ts | 46 ++-- src/sync/impl.ts | 373 ++++++++++++++++++--------------- src/sync/index.ts | 8 +- src/sync/types.ts | 81 +++---- src/types.ts | 15 +- src/utils.ts | 86 +------- tsconfig.json | 9 +- 24 files changed, 1702 insertions(+), 688 deletions(-) create mode 100644 src/collector/v2/impl.ts create mode 100644 src/collector/v2/index.ts create mode 100644 src/collector/v2/types.ts create mode 100644 src/comparer/async.ts create mode 100644 src/comparer/sync.ts create mode 100644 src/comparer/types.ts create mode 100644 src/equality-comparer/index.ts create mode 100644 src/equality-comparer/types.ts diff --git a/package-lock.json b/package-lock.json index 111db8b..00c9db9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,16 +9,16 @@ "version": "1.0.0", "license": "ISC", "devDependencies": { - "@types/node": "^20.14.15", - "esbuild": "^0.23.0", - "tsx": "^4.17.0", - "typescript": "^5.5.4" + "@types/node": "^22.15.21", + "esbuild": "^0.25.4", + "tsx": "^4.19.4", + "typescript": "^5.8.3" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.0.tgz", - "integrity": "sha512-3sG8Zwa5fMcA9bgqB8AfWPQ+HFke6uD3h1s3RIwUNK8EG7a4buxvuFTs3j1IMs2NXAk9F30C/FF4vxRgQCcmoQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", + "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", "cpu": [ "ppc64" ], @@ -33,9 +33,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.0.tgz", - "integrity": "sha512-+KuOHTKKyIKgEEqKbGTK8W7mPp+hKinbMBeEnNzjJGyFcWsfrXjSTNluJHCY1RqhxFurdD8uNXQDei7qDlR6+g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", + "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", "cpu": [ "arm" ], @@ -50,9 +50,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.0.tgz", - "integrity": "sha512-EuHFUYkAVfU4qBdyivULuu03FhJO4IJN9PGuABGrFy4vUuzk91P2d+npxHcFdpUnfYKy0PuV+n6bKIpHOB3prQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", + "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", "cpu": [ "arm64" ], @@ -67,9 +67,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.0.tgz", - "integrity": "sha512-WRrmKidLoKDl56LsbBMhzTTBxrsVwTKdNbKDalbEZr0tcsBgCLbEtoNthOW6PX942YiYq8HzEnb4yWQMLQuipQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", + "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", "cpu": [ "x64" ], @@ -84,9 +84,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.0.tgz", - "integrity": "sha512-YLntie/IdS31H54Ogdn+v50NuoWF5BDkEUFpiOChVa9UnKpftgwzZRrI4J132ETIi+D8n6xh9IviFV3eXdxfow==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", + "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", "cpu": [ "arm64" ], @@ -101,9 +101,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.0.tgz", - "integrity": "sha512-IMQ6eme4AfznElesHUPDZ+teuGwoRmVuuixu7sv92ZkdQcPbsNHzutd+rAfaBKo8YK3IrBEi9SLLKWJdEvJniQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", + "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", "cpu": [ "x64" ], @@ -118,9 +118,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.0.tgz", - "integrity": "sha512-0muYWCng5vqaxobq6LB3YNtevDFSAZGlgtLoAc81PjUfiFz36n4KMpwhtAd4he8ToSI3TGyuhyx5xmiWNYZFyw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", + "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", "cpu": [ "arm64" ], @@ -135,9 +135,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.0.tgz", - "integrity": "sha512-XKDVu8IsD0/q3foBzsXGt/KjD/yTKBCIwOHE1XwiXmrRwrX6Hbnd5Eqn/WvDekddK21tfszBSrE/WMaZh+1buQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", + "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", "cpu": [ "x64" ], @@ -152,9 +152,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.0.tgz", - "integrity": "sha512-SEELSTEtOFu5LPykzA395Mc+54RMg1EUgXP+iw2SJ72+ooMwVsgfuwXo5Fn0wXNgWZsTVHwY2cg4Vi/bOD88qw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", + "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", "cpu": [ "arm" ], @@ -169,9 +169,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.0.tgz", - "integrity": "sha512-j1t5iG8jE7BhonbsEg5d9qOYcVZv/Rv6tghaXM/Ug9xahM0nX/H2gfu6X6z11QRTMT6+aywOMA8TDkhPo8aCGw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", + "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", "cpu": [ "arm64" ], @@ -186,9 +186,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.0.tgz", - "integrity": "sha512-P7O5Tkh2NbgIm2R6x1zGJJsnacDzTFcRWZyTTMgFdVit6E98LTxO+v8LCCLWRvPrjdzXHx9FEOA8oAZPyApWUA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", + "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", "cpu": [ "ia32" ], @@ -203,9 +203,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.0.tgz", - "integrity": "sha512-InQwepswq6urikQiIC/kkx412fqUZudBO4SYKu0N+tGhXRWUqAx+Q+341tFV6QdBifpjYgUndV1hhMq3WeJi7A==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", + "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", "cpu": [ "loong64" ], @@ -220,9 +220,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.0.tgz", - "integrity": "sha512-J9rflLtqdYrxHv2FqXE2i1ELgNjT+JFURt/uDMoPQLcjWQA5wDKgQA4t/dTqGa88ZVECKaD0TctwsUfHbVoi4w==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", + "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", "cpu": [ "mips64el" ], @@ -237,9 +237,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.0.tgz", - "integrity": "sha512-cShCXtEOVc5GxU0fM+dsFD10qZ5UpcQ8AM22bYj0u/yaAykWnqXJDpd77ublcX6vdDsWLuweeuSNZk4yUxZwtw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", + "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", "cpu": [ "ppc64" ], @@ -254,9 +254,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.0.tgz", - "integrity": "sha512-HEtaN7Y5UB4tZPeQmgz/UhzoEyYftbMXrBCUjINGjh3uil+rB/QzzpMshz3cNUxqXN7Vr93zzVtpIDL99t9aRw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", + "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", "cpu": [ "riscv64" ], @@ -271,9 +271,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.0.tgz", - "integrity": "sha512-WDi3+NVAuyjg/Wxi+o5KPqRbZY0QhI9TjrEEm+8dmpY9Xir8+HE/HNx2JoLckhKbFopW0RdO2D72w8trZOV+Wg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", + "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", "cpu": [ "s390x" ], @@ -288,9 +288,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", + "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", "cpu": [ "x64" ], @@ -304,10 +304,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", + "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.0.tgz", - "integrity": "sha512-cRK+YDem7lFTs2Q5nEv/HHc4LnrfBCbH5+JHu6wm2eP+d8OZNoSMYgPZJq78vqQ9g+9+nMuIsAO7skzphRXHyw==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", + "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", "cpu": [ "x64" ], @@ -322,9 +339,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.0.tgz", - "integrity": "sha512-suXjq53gERueVWu0OKxzWqk7NxiUWSUlrxoZK7usiF50C6ipColGR5qie2496iKGYNLhDZkPxBI3erbnYkU0rQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", + "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", "cpu": [ "arm64" ], @@ -339,9 +356,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.0.tgz", - "integrity": "sha512-6p3nHpby0DM/v15IFKMjAaayFhqnXV52aEmv1whZHX56pdkK+MEaLoQWj+H42ssFarP1PcomVhbsR4pkz09qBg==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", + "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", "cpu": [ "x64" ], @@ -356,9 +373,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", + "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", "cpu": [ "x64" ], @@ -373,9 +390,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", + "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", "cpu": [ "arm64" ], @@ -390,9 +407,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", + "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", "cpu": [ "ia32" ], @@ -407,9 +424,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", + "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", "cpu": [ "x64" ], @@ -424,19 +441,19 @@ } }, "node_modules/@types/node": { - "version": "20.14.15", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.15.tgz", - "integrity": "sha512-Fz1xDMCF/B00/tYSVMlmK7hVeLh7jE5f3B7X1/hmV0MJBwE27KlS7EvD/Yp+z1lm8mVhwV5w+n8jOZG8AfTlKw==", + "version": "22.15.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.21.tgz", + "integrity": "sha512-EV/37Td6c+MgKAbkcLG6vqZ2zEYHD7bvSrzqqs2RIhbA6w3x+Dqz8MZM3sP6kGTeLrdoOgKZe+Xja7tUB2DNkQ==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~6.21.0" } }, "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "version": "0.25.4", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", + "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -447,30 +464,31 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "@esbuild/aix-ppc64": "0.25.4", + "@esbuild/android-arm": "0.25.4", + "@esbuild/android-arm64": "0.25.4", + "@esbuild/android-x64": "0.25.4", + "@esbuild/darwin-arm64": "0.25.4", + "@esbuild/darwin-x64": "0.25.4", + "@esbuild/freebsd-arm64": "0.25.4", + "@esbuild/freebsd-x64": "0.25.4", + "@esbuild/linux-arm": "0.25.4", + "@esbuild/linux-arm64": "0.25.4", + "@esbuild/linux-ia32": "0.25.4", + "@esbuild/linux-loong64": "0.25.4", + "@esbuild/linux-mips64el": "0.25.4", + "@esbuild/linux-ppc64": "0.25.4", + "@esbuild/linux-riscv64": "0.25.4", + "@esbuild/linux-s390x": "0.25.4", + "@esbuild/linux-x64": "0.25.4", + "@esbuild/netbsd-arm64": "0.25.4", + "@esbuild/netbsd-x64": "0.25.4", + "@esbuild/openbsd-arm64": "0.25.4", + "@esbuild/openbsd-x64": "0.25.4", + "@esbuild/sunos-x64": "0.25.4", + "@esbuild/win32-arm64": "0.25.4", + "@esbuild/win32-ia32": "0.25.4", + "@esbuild/win32-x64": "0.25.4" } }, "node_modules/fsevents": { @@ -512,13 +530,13 @@ } }, "node_modules/tsx": { - "version": "4.17.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.17.0.tgz", - "integrity": "sha512-eN4mnDA5UMKDt4YZixo9tBioibaMBpoxBkD+rIPAjVmYERSG0/dWEY1CEFuV89CgASlKL499q8AhmkMnnjtOJg==", + "version": "4.19.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.4.tgz", + "integrity": "sha512-gK5GVzDkJK1SI1zwHf32Mqxf2tSJkNx+eYcNly5+nHvWqXUJYUkWBQtKauoESz3ymezAI++ZwT855x5p5eop+Q==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "~0.23.0", + "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "bin": { @@ -532,9 +550,9 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -546,9 +564,9 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true, "license": "MIT" } diff --git a/package.json b/package.json index 0aeabfc..f9153aa 100644 --- a/package.json +++ b/package.json @@ -12,9 +12,9 @@ "author": "Hervé BECHER", "license": "ISC", "devDependencies": { - "@types/node": "^20.14.15", - "esbuild": "^0.23.0", - "tsx": "^4.17.0", - "typescript": "^5.5.4" + "@types/node": "^22.15.21", + "esbuild": "^0.25.4", + "tsx": "^4.19.4", + "typescript": "^5.8.3" } } diff --git a/src/async/impl.ts b/src/async/impl.ts index b24ad2d..5ef65ca 100644 --- a/src/async/impl.ts +++ b/src/async/impl.ts @@ -1,4 +1,8 @@ import { Collector } from "../collector/types.js"; +import { asAsyncComparer, combineNullableAsyncComparers, createAsyncComparerUsing, defaultAsyncComparer } from "../comparer/async.js"; +import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "../comparer/types.js"; +import { strictEquals, identity } from "../equality-comparer/index.js"; +import { MaybeAsyncEqualityComparison } from "../equality-comparer/types.js"; import { createAsyncEqualityMap } from "../equality-map.js"; import { createAsyncEqualitySet } from "../equality-set.js"; import { createQueue } from "../queue.js"; @@ -6,8 +10,8 @@ import { getRandomElementAsync } from "../random/index.js"; import { AsyncRandomOptions } from "../random/types.js"; import { selectionSorter } from "../sorting.js"; import { Sequence } from "../sync/types.js"; -import { MaybeAsyncAnyPredicate, MaybeAsyncConverter, MaybeAsyncEquater, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncComparer, MaybeAsyncAction, MaybePromiseLike, MaybeAsyncGenerator, MaybeAsyncSequence, MaybePromise } from "../types.js"; -import { strictEquals, identity, operatorCompare, defaultArrayComparer, combineAsyncComparers, asAsyncIterable } from "../utils.js"; +import { MaybeAsyncAnyPredicate, MaybeAsyncConverter, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncAction, MaybePromiseLike, MaybeAsyncGenerator, MaybePromise, MaybeAsyncIterable, MaybeAsyncTypePredicate } from "../types.js"; +import { asAsyncIterable } from "../utils.js"; import { array, empty, wrap } from "./index.js"; import { AsyncSequence, AsyncSequencePipeline, GroupedAsyncSequence, OrderedAsyncSequence } from "./types.js"; @@ -28,33 +32,35 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return new SelectAsyncSequence(this, converter); } - selectMany(converter: MaybeAsyncConverter>): AsyncSequence { + selectMany(converter: MaybeAsyncConverter>): AsyncSequence { return new SelectManyAsyncSequence(this, converter); } - where(predicate: MaybeAsyncAnyPredicate): AsyncSequence { - return new WhereAsyncSequence(this, predicate); + where(predicate: MaybeAsyncTypePredicate): AsyncSequence; + where(predicate: MaybeAsyncAnyPredicate): AsyncSequence; + where(predicate: any) { + return new WhereAsyncSequence(this, predicate); } - groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; - groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; groupBy(keySelector: any, elementSelector?: any, keyComparer?: any) { return new GroupByAsyncSequence(this, keySelector, elementSelector, keyComparer); } - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence<[TElement, TOther]>; - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence<[TElement, TOther]>; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence; join(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return new JoinAsyncSequence(this, wrap(sequence), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence; groupJoin(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return new GroupJoinAsyncSequence(this, wrap(sequence), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - async contains(obj: TElement, equater?: MaybeAsyncEquater) { + async contains(obj: TElement, equater?: MaybeAsyncEqualityComparison) { if (!equater) { equater = strictEquals; } @@ -68,7 +74,7 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return false; } - async sequenceEquals(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater) { + async sequenceEquals(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison) { if (this === sequence) { return true; } @@ -115,11 +121,11 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return new PrependAsyncSequence(this, obj); } - remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEquater): AsyncSequence { + remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEqualityComparison): AsyncSequence { return new RemoveAsyncSequence(this, obj, all, equater); } - concat(...sequences: MaybeAsyncSequence[]): AsyncSequence { + concat(...sequences: MaybeAsyncIterable[]): AsyncSequence { if (sequences.length === 0) { return this; } @@ -391,7 +397,7 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return acc as unknown as TResult; } - async #find(sorter: MaybeAsyncAnyPredicate, selector?: MaybeAsyncConverter, comparer?: MaybeAsyncComparer) { + async #find(sorter: MaybeAsyncAnyPredicate, selector?: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer) { const iterator = this.iterator(); let next = await iterator.next(); @@ -404,9 +410,7 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im selector = identity as MaybeAsyncConverter; } - if (!comparer) { - comparer = operatorCompare; - } + comparer = comparer ? asAsyncComparer(comparer) : defaultAsyncComparer; let result = next.value; let convertedResult = await selector(result); @@ -421,7 +425,7 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im const value = next.value; const convertedValue = await selector(value); - if (await sorter(await comparer(convertedResult, convertedValue))) { + if (await sorter(await comparer.compare(convertedResult, convertedValue))) { result = value; convertedResult = convertedValue; } @@ -430,75 +434,125 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im return result; } - min(comparer?: MaybeAsyncComparer) { + min(comparer?: MaybeAsyncComparisonOrComparer) { return this.#find(x => x > 0, undefined, comparer); } - minBy(converter: MaybeAsyncConverter, comparer?: MaybeAsyncComparer) { + minBy(converter: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer) { return this.#find(x => x > 0, converter, comparer); } - max(comparer?: MaybeAsyncComparer) { + max(comparer?: MaybeAsyncComparisonOrComparer) { return this.#find(x => x < 0, undefined, comparer); } - maxBy(converter: MaybeAsyncConverter, comparer?: MaybeAsyncComparer) { + maxBy(converter: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer) { return this.#find(x => x < 0, converter, comparer); } - order(comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + async #findBounds(selector?: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): Promise<[min: TElement, max: TElement]> { + const iterator = this.iterator(); + + let next = await iterator.next(); + + if (next.done) { + throw new Error("Sequence contains no element."); + } + + if (!selector) { + selector = identity as MaybeAsyncConverter; + } + + comparer = comparer ? asAsyncComparer(comparer) : defaultAsyncComparer; + + let minBound = next.value, maxBound = minBound; + let convertedMinBound = await selector(minBound), convertedMaxBound = convertedMinBound; + + while (true) { + next = await iterator.next(); + + if (next.done) { + break; + } + + const value = next.value; + const convertedValue = await selector(value); + + if (await comparer.compare(convertedMinBound, convertedValue) > 0) { + minBound = value; + convertedMinBound = convertedValue; + } + + if (await comparer.compare(convertedMaxBound, convertedValue) < 0) { + maxBound = value; + convertedMaxBound = convertedValue; + } + } + + return [minBound, maxBound]; + } + + bounds(comparer?: MaybeAsyncComparisonOrComparer) { + return this.#findBounds(undefined, comparer); + } + + boundsBy(converter: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer) { + return this.#findBounds(converter, comparer); + } + + order(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new OrderAsyncSequence(this, false, comparer); } - orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new OrderByAsyncSequence(this, false, selector, comparer); } - orderDescending(comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + orderDescending(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new OrderAsyncSequence(this, true, comparer); } - orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new OrderByAsyncSequence(this, true, selector, comparer); } - partition(equater?: MaybeAsyncEquater): AsyncSequence> { + partition(equater?: MaybeAsyncEqualityComparison): AsyncSequence> { return new PartitionAsyncSequence(this, equater); } - partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence> { + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence> { return new PartitionByAsyncSequence(this, selector, equater); } - distinct(equater?: MaybeAsyncEquater): AsyncSequence { + distinct(equater?: MaybeAsyncEqualityComparison): AsyncSequence { return new DistinctAsyncSequence(this, equater); } - distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence { + distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence { return new DistinctByAsyncSequence(this, selector, equater); } - union(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater): AsyncSequence { + union(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison): AsyncSequence { return new UnionAsyncSequence(this, wrap(sequence), equater); } - unionBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence { + unionBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence { return new UnionByAsyncSequence(this, wrap(sequence), selector, equater); } - except(sequence: MaybeAsyncSequence): AsyncSequence { + except(sequence: MaybeAsyncIterable): AsyncSequence { return new ExceptAsyncSequence(this, wrap(sequence)); } - exceptBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter): AsyncSequence { + exceptBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncSequence { return new ExceptByAsyncSequence(this, wrap(sequence), selector); } - intersect(sequence: MaybeAsyncSequence): AsyncSequence { + intersect(sequence: MaybeAsyncIterable): AsyncSequence { return new IntersectAsyncSequence(this, wrap(sequence)); } - intersectBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter): AsyncSequence { + intersectBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter): AsyncSequence { return new IntersectByAsyncSequence(this, wrap(sequence), selector); } @@ -608,7 +662,7 @@ export abstract class BaseAsyncSequence extends AsyncSequenceMarker im } } - zip(sequence: MaybeAsyncSequence): AsyncSequence<[TElement, TOther]> { + zip(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]> { return new ZippedAsyncSequence(this, wrap(sequence)); } @@ -753,37 +807,39 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.select(selector); } - selectMany(selector: MaybeAsyncConverter>): AsyncSequence { + selectMany(selector: MaybeAsyncConverter>): AsyncSequence { return this.#sequence.selectMany(selector); } - where(predicate: MaybeAsyncAnyPredicate): AsyncSequence { + where(predicate: MaybeAsyncTypePredicate): AsyncSequence; + where(predicate: MaybeAsyncAnyPredicate): AsyncSequence; + where(predicate: any) { return this.#sequence.where(predicate); } - groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; - groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; groupBy(keySelector: any, elementSelector?: any, keyComparer?: any) { return this.#sequence.groupBy(keySelector, elementSelector, keyComparer); } - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence<[TElement, TOther]>; - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence<[TElement, TOther]>; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence; join(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.join(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence>; - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater | undefined): AsyncSequence; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence>; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEqualityComparison | undefined): AsyncSequence; groupJoin(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.groupJoin(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - contains(obj: TElement, equater?: MaybeAsyncEquater | undefined): Promise { + contains(obj: TElement, equater?: MaybeAsyncEqualityComparison | undefined): Promise { return this.#sequence.contains(obj, equater); } - sequenceEquals(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater | undefined): Promise { + sequenceEquals(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison | undefined): Promise { return this.#sequence.sequenceEquals(sequence, equater); } @@ -795,11 +851,11 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.prepend(obj); } - remove(obj: TElement, all?: boolean | undefined, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + remove(obj: TElement, all?: boolean | undefined, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.remove(obj, all, equater); } - concat(...sequences: MaybeAsyncSequence[]): AsyncSequence { + concat(...sequences: MaybeAsyncIterable[]): AsyncSequence { return this.#sequence.concat(...sequences); } @@ -858,59 +914,67 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.maxBy(selector); } - order(comparer?: MaybeAsyncComparer | undefined): AsyncSequence { + bounds(comparer?: MaybeAsyncComparisonOrComparer | undefined): Promise<[min: TElement, max: TElement]> { + return this.#sequence.bounds(comparer); + } + + boundsBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer | undefined): Promise<[min: TElement, max: TElement]> { + return this.#sequence.boundsBy(selector, comparer); + } + + order(comparer?: MaybeAsyncComparisonOrComparer | undefined): AsyncSequence { return this.#sequence.order(comparer); } - orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer | undefined): AsyncSequence { + orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer | undefined): AsyncSequence { return this.#sequence.orderBy(selector, comparer); } - orderDescending(comparer?: MaybeAsyncComparer | undefined): AsyncSequence { + orderDescending(comparer?: MaybeAsyncComparisonOrComparer | undefined): AsyncSequence { return this.#sequence.orderDescending(comparer); } - orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer | undefined): AsyncSequence { + orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer | undefined): AsyncSequence { return this.#sequence.orderByDescending(selector, comparer); } - partition(equater?: MaybeAsyncEquater | undefined): AsyncSequence> { + partition(equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence> { return this.#sequence.partition(equater); } - partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence> { + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence> { return this.#sequence.partitionBy(selector, equater); } - distinct(equater?: MaybeAsyncEquater | undefined): AsyncSequence { + distinct(equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.distinct(equater); } - distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.distinctBy(selector, equater); } - union(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + union(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.union(wrap(sequence), equater); } - unionBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + unionBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.unionBy(wrap(sequence), selector, equater); } - except(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + except(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.except(wrap(sequence), equater); } - exceptBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + exceptBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.exceptBy(wrap(sequence), selector, equater); } - intersect(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + intersect(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.intersect(wrap(sequence), equater); } - intersectBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater | undefined): AsyncSequence { + intersectBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison | undefined): AsyncSequence { return this.#sequence.intersectBy(wrap(sequence), selector, equater); } @@ -962,7 +1026,7 @@ export class DelegatedAsyncSequence extends AsyncSequenceMarker implem return this.#sequence.forEach(action); } - zip(sequence: MaybeAsyncSequence): AsyncSequence<[TElement, TOther]> { + zip(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]> { return this.#sequence.zip(wrap(sequence)); } @@ -1033,14 +1097,14 @@ export class GroupedAsyncSequenceImpl extends DelegatedAsyncSequ abstract class BaseOrderedAsyncSequence extends BaseAsyncSequence implements OrderedAsyncSequence { readonly #sequence: AsyncSequence; - readonly #sorter: MaybeAsyncComparer; + readonly #sorter: AsyncComparer; readonly #descending: boolean; - constructor(sequence: AsyncSequence, sorter: MaybeAsyncComparer, descending: boolean) { + constructor(sequence: AsyncSequence, sorter: MaybeAsyncComparisonOrComparer | undefined, descending: boolean) { super(); this.#sequence = sequence; - this.#sorter = sorter; + this.#sorter = sorter ? asAsyncComparer(sorter) : defaultAsyncComparer; this.#descending = descending; } @@ -1056,19 +1120,19 @@ abstract class BaseOrderedAsyncSequence extends BaseAsyncSequence): OrderedAsyncSequence { + thenSelf(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new ThenOrderAsyncSequence(this, false, comparer); } - thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new ThenOrderByAsyncSequence(this, false, selector, comparer); } - thenSelfDescending(comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + thenSelfDescending(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new ThenOrderAsyncSequence(this, true, comparer); } - thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence { + thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence { return new ThenOrderByAsyncSequence(this, true, selector, comparer); } @@ -1079,7 +1143,7 @@ abstract class BaseOrderedAsyncSequence extends BaseAsyncSequence extends BaseAsyncSequence { class DistinctAsyncSequence extends BaseAsyncSequence { readonly #sequence: AsyncSequence; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(sequence: AsyncSequence, equater?: MaybeAsyncEquater) { + constructor(sequence: AsyncSequence, equater?: MaybeAsyncEqualityComparison) { super(); this.#sequence = sequence; @@ -1350,9 +1414,9 @@ class DistinctAsyncSequence extends BaseAsyncSequence { class DistinctByAsyncSequence extends BaseAsyncSequence { readonly #sequence: AsyncSequence; readonly #selector: MaybeAsyncConverter; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(sequence: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater) { + constructor(sequence: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison) { super(); this.#sequence = sequence; @@ -1373,11 +1437,11 @@ class DistinctByAsyncSequence extends BaseAsyncSequence { } } -class WhereAsyncSequence extends BaseAsyncSequence { - readonly #sequence: AsyncSequence; - readonly #predicate: MaybeAsyncAnyPredicate; +class WhereAsyncSequence extends BaseAsyncSequence { + readonly #sequence: AsyncSequence; + readonly #predicate: MaybeAsyncTypePredicate; - constructor(sequence: AsyncSequence, predicate: MaybeAsyncAnyPredicate) { + constructor(sequence: AsyncSequence, predicate: MaybeAsyncTypePredicate) { super(); this.#sequence = sequence; @@ -1387,7 +1451,7 @@ class WhereAsyncSequence extends BaseAsyncSequence { override async *iterator() { for await (const obj of this.#sequence) { if (await this.#predicate(obj)) { - yield obj; + yield obj as TFiltered; } } } @@ -1395,9 +1459,9 @@ class WhereAsyncSequence extends BaseAsyncSequence { class SelectManyAsyncSequence extends BaseAsyncSequence { readonly #sequence: AsyncSequence; - readonly #converter: MaybeAsyncConverter>; + readonly #converter: MaybeAsyncConverter>; - constructor(sequence: AsyncSequence, converter: MaybeAsyncConverter>) { + constructor(sequence: AsyncSequence, converter: MaybeAsyncConverter>) { super(); this.#sequence = sequence; @@ -1640,37 +1704,26 @@ class TakeAsyncSequence extends BaseAsyncSequence { } class OrderAsyncSequence extends BaseOrderedAsyncSequence { - constructor(sequence: AsyncSequence, descending: boolean, sorter?: MaybeAsyncComparer) { - super(sequence, sorter ?? defaultArrayComparer, descending); + constructor(sequence: AsyncSequence, descending: boolean, sorter?: MaybeAsyncComparisonOrComparer) { + super(sequence, sorter, descending); } } class OrderByAsyncSequence extends BaseOrderedAsyncSequence { - constructor(sequence: AsyncSequence, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { - super(sequence, OrderByAsyncSequence.#createSorter(selector, sorter), descending); - } - - static #createSorter(selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { - sorter ??= defaultArrayComparer; - return async (a: T, b: T) => sorter(await selector(a), await selector(b)); + constructor(sequence: AsyncSequence, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparisonOrComparer) { + super(sequence, createAsyncComparerUsing(selector, sorter), descending); } } class ThenOrderAsyncSequence extends BaseOrderedAsyncSequence { - constructor(sequence: OrderedAsyncSequence, descending: boolean, sorter?: MaybeAsyncComparer) { - super(sequence, combineAsyncComparers(sequence.comparer ?? defaultArrayComparer, sorter ?? defaultArrayComparer), descending); + constructor(sequence: OrderedAsyncSequence, descending: boolean, sorter?: MaybeAsyncComparisonOrComparer) { + super(sequence, combineNullableAsyncComparers([sequence.comparer, sorter]), descending); } } class ThenOrderByAsyncSequence extends BaseOrderedAsyncSequence { - constructor(sequence: OrderedAsyncSequence, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { - super(sequence, ThenOrderByAsyncSequence.#createCombinedSorter(sequence.comparer, selector, sorter), descending); - } - - static #createCombinedSorter(baseSorter: MaybeAsyncComparer, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparer) { - baseSorter ??= defaultArrayComparer; - sorter ??= defaultArrayComparer; - return combineAsyncComparers(baseSorter, async (a: T, b: T) => sorter(await selector(a), await selector(b))); + constructor(sequence: OrderedAsyncSequence, descending: boolean, selector: MaybeAsyncConverter, sorter?: MaybeAsyncComparisonOrComparer) { + super(sequence, combineNullableAsyncComparers([sequence.comparer, createAsyncComparerUsing(selector, sorter)]), descending); } } @@ -1783,9 +1836,9 @@ class ZippedAsyncSequence extends BaseAsyncSequence<[T, U]> { class UnionAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1816,9 +1869,9 @@ class UnionByAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; readonly #selector: MaybeAsyncConverter; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1849,9 +1902,9 @@ class UnionByAsyncSequence extends BaseAsyncSequence { class ExceptAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1880,9 +1933,9 @@ class ExceptByAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; readonly #selector: MaybeAsyncConverter; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1911,9 +1964,9 @@ class ExceptByAsyncSequence extends BaseAsyncSequence { class IntersectAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1942,9 +1995,9 @@ class IntersectByAsyncSequence extends BaseAsyncSequence { readonly #first: AsyncSequence; readonly #second: AsyncSequence; readonly #selector: MaybeAsyncConverter; - readonly #equater: MaybeAsyncEquater | undefined; + readonly #equater: MaybeAsyncEqualityComparison | undefined; - constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -1996,9 +2049,9 @@ class GroupByAsyncSequence extends BaseAsyncSequence; readonly #keySelector: MaybeAsyncConverter; readonly #elementSelector: MaybeAsyncConverter; - readonly #keyComparer: MaybeAsyncEquater | undefined; + readonly #keyComparer: MaybeAsyncEqualityComparison | undefined; - constructor(sequence: AsyncSequence, keySelector: MaybeAsyncConverter, elementSelector?: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater) { + constructor(sequence: AsyncSequence, keySelector: MaybeAsyncConverter, elementSelector?: MaybeAsyncConverter, keyComparer?: MaybeAsyncEqualityComparison) { super(); this.#sequence = sequence; @@ -2069,9 +2122,9 @@ class JoinAsyncSequence extends BaseAsyncSequence readonly #firstKeySelector: MaybeAsyncConverter; readonly #secondKeySelector: MaybeAsyncConverter; readonly #resultSelector: MaybeAsyncBiConverter; - readonly #keyComparer: MaybeAsyncEquater; + readonly #keyComparer: MaybeAsyncEqualityComparison; - constructor(first: AsyncSequence, second: AsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEqualityComparison) { super(); this.#first = first; @@ -2102,17 +2155,17 @@ class GroupJoinAsyncSequence extends BaseAsyncSeq readonly #second: AsyncSequence; readonly #firstKeySelector: MaybeAsyncConverter; readonly #secondKeySelector: MaybeAsyncConverter; - readonly #resultSelector: MaybeAsyncBiConverter, TResult>; - readonly #keyComparer: MaybeAsyncEquater; + readonly #resultSelector: MaybeAsyncBiConverter, TResult>; + readonly #keyComparer: MaybeAsyncEqualityComparison; - constructor(first: AsyncSequence, second: AsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater) { + constructor(first: AsyncSequence, second: AsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEqualityComparison) { super(); this.#first = first; this.#second = second; this.#firstKeySelector = firstKeySelector; this.#secondKeySelector = secondKeySelector; - this.#resultSelector = resultSelector ?? GroupJoinAsyncSequence.#defaultResultSelector as MaybeAsyncBiConverter, TResult>; + this.#resultSelector = resultSelector ?? GroupJoinAsyncSequence.#defaultResultSelector as MaybeAsyncBiConverter, TResult>; this.#keyComparer = keyComparer ?? strictEquals; } @@ -2133,7 +2186,6 @@ class GroupJoinAsyncSequence extends BaseAsyncSeq } } - // yield this.#resultSelector(firstObj, this.#second.where(secondObj => this.#keyComparer(firstKey, this.#secondKeySelector(secondObj)))); yield this.#resultSelector(firstObj, array(secondObjs)); } } @@ -2143,9 +2195,9 @@ class RemoveAsyncSequence extends BaseAsyncSequence { readonly #sequence: AsyncSequence; readonly #obj: T; readonly #all: boolean; - readonly #equater: MaybeAsyncEquater; + readonly #equater: MaybeAsyncEqualityComparison; - constructor(sequence: AsyncSequence, obj: T, all?: boolean, equater?: MaybeAsyncEquater) { + constructor(sequence: AsyncSequence, obj: T, all?: boolean, equater?: MaybeAsyncEqualityComparison) { super(); this.#sequence = sequence; @@ -2202,9 +2254,9 @@ class CacheAsyncSequence extends BaseAsyncSequence { class PartitionAsyncSequence extends BaseAsyncSequence> { readonly #sequence: AsyncSequence; - readonly #equater: MaybeAsyncEquater; + readonly #equater: MaybeAsyncEqualityComparison; - constructor(sequence: AsyncSequence, equater: MaybeAsyncEquater | undefined) { + constructor(sequence: AsyncSequence, equater: MaybeAsyncEqualityComparison | undefined) { super(); this.#sequence = sequence; @@ -2233,9 +2285,9 @@ class PartitionAsyncSequence extends BaseAsyncSequence> { class PartitionByAsyncSequence extends BaseAsyncSequence> { readonly #sequence: AsyncSequence; readonly #selector: MaybeAsyncConverter; - readonly #equater: MaybeAsyncEquater; + readonly #equater: MaybeAsyncEqualityComparison; - constructor(sequence: AsyncSequence, selector: MaybeAsyncConverter, equater: MaybeAsyncEquater | undefined) { + constructor(sequence: AsyncSequence, selector: MaybeAsyncConverter, equater: MaybeAsyncEqualityComparison | undefined) { super(); this.#sequence = sequence; diff --git a/src/async/types.ts b/src/async/types.ts index 89c2857..71e841c 100644 --- a/src/async/types.ts +++ b/src/async/types.ts @@ -1,6 +1,8 @@ import { Collector } from "../collector/types.js"; +import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "../comparer/types.js"; +import { MaybeAsyncEqualityComparison } from "../equality-comparer/types.js"; import { AsyncRandomOptions } from "../random/types.js"; -import { MaybeAsyncAnyPredicate, MaybeAsyncConverter, MaybeAsyncEquater, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncComparer, MaybeAsyncAction, MaybeAsyncSequence, MaybeAsyncFunction, MaybePromise } from "../types.js"; +import { MaybeAsyncAnyPredicate, MaybeAsyncConverter, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncAction, MaybeAsyncFunction, MaybePromise, MaybeAsyncIterable } from "../types.js"; export type AsyncSequencePipeline = MaybeAsyncFunction<(sequence: AsyncSequence) => TResult>; @@ -15,30 +17,31 @@ export interface AsyncSequence extends AsyncIterable { maxCount(): Promise; select(selector: MaybeAsyncConverter): AsyncSequence; - selectMany(selector: MaybeAsyncConverter>): AsyncSequence; + selectMany(selector: MaybeAsyncConverter>): AsyncSequence; + where(predicate: MaybeAsyncAnyPredicate): AsyncSequence; where(predicate: MaybeAsyncAnyPredicate): AsyncSequence; - groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncSequence>; - groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEquater): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence>; + groupBy(keySelector: MaybeAsyncConverter, elementSelector: MaybeAsyncConverter, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence>; - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncSequence<[TElement, TOther]>; - join(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEquater): AsyncSequence; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence<[TElement, TOther]>; + join(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence; - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEquater): AsyncSequence>; - groupJoin(sequence: MaybeAsyncSequence, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEquater): AsyncSequence; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence>; + groupJoin(sequence: MaybeAsyncIterable, firstKeySelector: MaybeAsyncConverter, secondKeySelector: MaybeAsyncConverter, resultSelector: MaybeAsyncBiConverter, TResult>, keyComparer?: MaybeAsyncEqualityComparison): AsyncSequence; - contains(obj: TElement, equater?: MaybeAsyncEquater): Promise; + contains(obj: TElement, equater?: MaybeAsyncEqualityComparison): Promise; - sequenceEquals(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater): Promise; + sequenceEquals(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison): Promise; append(obj: TElement): AsyncSequence; prepend(obj: TElement): AsyncSequence; - remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEquater): AsyncSequence; + remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEqualityComparison): AsyncSequence; - concat(...sequences: MaybeAsyncSequence[]): AsyncSequence; + concat(...sequences: MaybeAsyncIterable[]): AsyncSequence; first(predicate?: MaybeAsyncAnyPredicate): Promise; firstOrDefault(predicate?: MaybeAsyncAnyPredicate, def?: TElement): Promise; @@ -62,26 +65,29 @@ export interface AsyncSequence extends AsyncIterable { max(): Promise; maxBy(selector: MaybeAsyncConverter): Promise; - order(comparer?: MaybeAsyncComparer): AsyncSequence; - orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncSequence; + bounds(comparer?: MaybeAsyncComparisonOrComparer): Promise<[min: TElement, max: TElement]>; + boundsBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): Promise<[min: TElement, max: TElement]>; - orderDescending(comparer?: MaybeAsyncComparer): AsyncSequence; - orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): AsyncSequence; + order(comparer?: MaybeAsyncComparisonOrComparer): AsyncSequence; + orderBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): AsyncSequence; - partition(equater?: MaybeAsyncEquater): AsyncSequence>; - partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence>; + orderDescending(comparer?: MaybeAsyncComparisonOrComparer): AsyncSequence; + orderByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): AsyncSequence; - distinct(equater?: MaybeAsyncEquater): AsyncSequence; - distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence; + partition(equater?: MaybeAsyncEqualityComparison): AsyncSequence>; + partitionBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence>; - union(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater): AsyncSequence; - unionBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence; + distinct(equater?: MaybeAsyncEqualityComparison): AsyncSequence; + distinctBy(selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence; - except(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater): AsyncSequence; - exceptBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence; + union(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison): AsyncSequence; + unionBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence; - intersect(sequence: MaybeAsyncSequence, equater?: MaybeAsyncEquater): AsyncSequence; - intersectBy(sequence: MaybeAsyncSequence, selector: MaybeAsyncConverter, equater?: MaybeAsyncEquater): AsyncSequence; + except(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison): AsyncSequence; + exceptBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence; + + intersect(sequence: MaybeAsyncIterable, equater?: MaybeAsyncEqualityComparison): AsyncSequence; + intersectBy(sequence: MaybeAsyncIterable, selector: MaybeAsyncConverter, equater?: MaybeAsyncEqualityComparison): AsyncSequence; all(predicate: MaybeAsyncAnyPredicate): Promise; any(predicate: MaybeAsyncAnyPredicate): Promise; @@ -101,7 +107,7 @@ export interface AsyncSequence extends AsyncIterable { forEach(action: MaybeAsyncAction): Promise; - zip(sequence: MaybeAsyncSequence): AsyncSequence<[TElement, TOther]>; + zip(sequence: MaybeAsyncIterable): AsyncSequence<[TElement, TOther]>; indexed(): AsyncSequence<[number, TElement]>; @@ -131,13 +137,13 @@ export interface GroupedAsyncSequence extends AsyncSequence extends AsyncSequence { - get comparer(): MaybeAsyncComparer; + get comparer(): AsyncComparer; - thenSelf(comparer?: MaybeAsyncComparer): OrderedAsyncSequence; + thenSelf(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence; - thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence; + thenBy(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence; - thenSelfDescending(comparer?: MaybeAsyncComparer): OrderedAsyncSequence; + thenSelfDescending(comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence; - thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparer): OrderedAsyncSequence; + thenByDescending(selector: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): OrderedAsyncSequence; } diff --git a/src/collector/impl.ts b/src/collector/impl.ts index cf43ee9..e4c0e03 100644 --- a/src/collector/impl.ts +++ b/src/collector/impl.ts @@ -65,6 +65,37 @@ export class ToObjectCollector imple } } +export class ToObjectListCollector implements Collector, Record> { + readonly #keySelector: Converter; + readonly #valueSelector: Converter; + + constructor(keySelector: Converter, valueSelector: Converter) { + this.#keySelector = keySelector; + this.#valueSelector = valueSelector; + } + + initialize() { + return {} as Record; + } + + accumulate(accumulator: Record, element: TElement) { + const key = this.#keySelector(element); + const value = this.#valueSelector(element); + + const l = accumulator[key]; + + if (l) { + l.push(value); + } else { + accumulator[key] = [value]; + } + } + + finalize(accumulator: Record) { + return accumulator; + } +} + export class ToMapCollector implements Collector, Map> { readonly #keySelector: Converter; readonly #valueSelector: Converter; @@ -90,6 +121,37 @@ export class ToMapCollector implements Collector implements Collector, Map> { + readonly #keySelector: Converter; + readonly #valueSelector: Converter; + + constructor(keySelector: Converter, valueSelector: Converter) { + this.#keySelector = keySelector; + this.#valueSelector = valueSelector; + } + + initialize() { + return new Map(); + } + + accumulate(accumulator: Map, element: TElement) { + const key = this.#keySelector(element); + const value = this.#valueSelector(element); + + const l = accumulator.get(key); + + if (l) { + l.push(value); + } else { + accumulator.set(key, [value]); + } + } + + finalize(accumulator: Map) { + return accumulator; + } +} + export class ToSetCollector implements Collector, Set> { initialize() { return new Set(); @@ -128,60 +190,60 @@ export class JoinCollector implements Collector { } } -export class SumCollector implements Collector { +export class SumCollector implements Collector { initialize() { return { sum: 0 }; } - accumulate(accumulator: { sum: number }, element: number) { + accumulate(accumulator: { sum: number; }, element: number) { accumulator.sum += element; } - finalize(accumulator: { sum: number }) { + finalize(accumulator: { sum: number; }) { return accumulator.sum; } } -export class BigIntSumCollector implements Collector { +export class BigIntSumCollector implements Collector { initialize() { return { sum: 0n, }; } - accumulate(accumulator: { sum: bigint }, element: bigint) { + accumulate(accumulator: { sum: bigint; }, element: bigint) { accumulator.sum += element; } - finalize(accumulator: { sum: bigint }) { + finalize(accumulator: { sum: bigint; }) { return accumulator.sum; } } -export class AverageCollector implements Collector { +export class AverageCollector implements Collector { initialize() { return { count: 0, sum: 0 }; } - accumulate(accumulator: { count: number, sum: number }, element: number) { + accumulate(accumulator: { count: number, sum: number; }, element: number) { accumulator.count++; accumulator.sum += element; } - finalize(accumulator: { count: number, sum: number }) { + finalize(accumulator: { count: number, sum: number; }) { return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count; } } -export class BigIntAverageCollector implements Collector { +export class BigIntAverageCollector implements Collector { initialize() { return { count: 0, sum: 0n }; } - accumulate(accumulator: { count: number, sum: bigint }, element: bigint) { + accumulate(accumulator: { count: number, sum: bigint; }, element: bigint) { accumulator.count++; accumulator.sum += element; } - finalize(accumulator: { count: number, sum: bigint }) { + finalize(accumulator: { count: number, sum: bigint; }) { return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count); } } diff --git a/src/collector/index.ts b/src/collector/index.ts index 2e9ef9b..2ea63fa 100644 --- a/src/collector/index.ts +++ b/src/collector/index.ts @@ -1,5 +1,5 @@ import { Converter } from "../types.js"; -import { SimpleCollector, ToArrayCollector, ToObjectCollector, ToMapCollector, ToSetCollector, JoinCollector, SumCollector, BigIntSumCollector, AverageCollector, BigIntAverageCollector } from "./impl.js"; +import { SimpleCollector, ToArrayCollector, ToObjectCollector, ToMapCollector, ToSetCollector, JoinCollector, SumCollector, BigIntSumCollector, AverageCollector, BigIntAverageCollector, ToObjectListCollector, ToMapListCollector } from "./impl.js"; import { Collector } from "./types.js"; export function create(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void, @@ -17,10 +17,18 @@ export function toObject(keySelector return new ToObjectCollector(keySelector, valueSelector); } +export function toObjectList(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToObjectListCollector(keySelector, valueSelector); +} + export function toMap(keySelector: Converter, valueSelector: Converter): Collector> { return new ToMapCollector(keySelector, valueSelector); } +export function toMapList(keySelector: Converter, valueSelector: Converter): Collector> { + return new ToMapListCollector(keySelector, valueSelector); +} + const toSetCollector = new ToSetCollector(); export function toSet(): Collector> { diff --git a/src/collector/v2/impl.ts b/src/collector/v2/impl.ts new file mode 100644 index 0000000..4a839f7 --- /dev/null +++ b/src/collector/v2/impl.ts @@ -0,0 +1,219 @@ +// import { Converter } from "../../types.js"; +// import { CollectorFactory, CollectorInstance } from "./types.js"; + +// export class ToArrayCollectorFactory implements CollectorFactory { +// initialize() { +// return new ToArrayCollectorInstance(); +// } +// }; + +// class ToArrayCollectorInstance implements CollectorInstance { +// readonly #accumulator: TElement[]; + +// constructor() { +// this.#accumulator = []; +// } + +// accumulate(element: TElement) { +// this.#accumulator.push(element); +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class ToObjectCollectorFactory implements CollectorFactory> { +// readonly #keySelector: Converter; +// readonly #valueSelector: Converter; + +// constructor(keySelector: Converter, valueSelector: Converter) { +// this.#keySelector = keySelector; +// this.#valueSelector = valueSelector; +// } + +// get keySelector() { +// return this.#keySelector; +// } + +// get valueSelector() { +// return this.#valueSelector; +// } + +// initialize() { +// return new ToObjectCollectorInstance(this); +// } +// } + +// class ToObjectCollectorInstance implements CollectorInstance> { +// readonly #factory: ToObjectCollectorFactory; +// readonly #accumulator = >{}; + +// constructor(factory: ToObjectCollectorFactory) { +// this.#factory = factory; +// } + +// accumulate(element: TElement) { +// const key = this.#factory.keySelector(element); +// const value = this.#factory.valueSelector(element); + +// this.#accumulator[key] = value; +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class ToMapCollectorFactory implements CollectorFactory> { +// readonly #keySelector: Converter; +// readonly #valueSelector: Converter; + +// constructor(keySelector: Converter, valueSelector: Converter) { +// this.#keySelector = keySelector; +// this.#valueSelector = valueSelector; +// } + +// get keySelector() { +// return this.#keySelector; +// } + +// get valueSelector() { +// return this.#valueSelector; +// } + +// initialize() { +// return new ToMapCollectorInstance(this); +// } +// } + +// class ToMapCollectorInstance implements CollectorInstance> { +// readonly #factory: ToMapCollectorFactory; +// readonly #accumulator = new Map(); + +// constructor(factory: ToMapCollectorFactory) { +// this.#factory = factory; +// } + +// accumulate(element: TElement) { +// const key = this.#factory.keySelector(element); +// const value = this.#factory.valueSelector(element); + +// this.#accumulator.set(key, value); +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class ToSetCollectorFactory implements CollectorFactory> { +// initialize() { +// return new ToSetCollectorInstance(); +// } +// } + +// class ToSetCollectorInstance implements CollectorInstance> { +// readonly #accumulator = new Set(); + +// accumulate(element: TElement) { +// this.#accumulator.add(element); +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class JoinCollectorFactory implements CollectorFactory { +// readonly #delimiter: string; +// readonly #prefix: string; +// readonly #suffix: string; + +// constructor(delimiter?: string, prefix?: string, suffix?: string) { +// this.#delimiter = delimiter ?? ""; +// this.#prefix = prefix ?? ""; +// this.#suffix = suffix ?? ""; +// } + +// accumulate(element: string) { +// this.#accumulator.push(element); +// } + +// finalize() { +// return this.#prefix + this.#accumulator.join(this.#delimiter) + this.#suffix; +// } +// } + +// class JoinCollectorInstance implements CollectorInstance { +// readonly #delimiter: string; +// readonly #prefix: string; +// readonly #suffix: string; +// readonly #accumulator: any[] = []; + +// constructor(delimiter: string, prefix: string, suffix: string) { +// this.#delimiter = delimiter ?? ""; +// this.#prefix = prefix ?? ""; +// this.#suffix = suffix ?? ""; +// } + +// accumulate(element: string) { +// this.#accumulator.push(element); +// } + +// finalize() { +// return this.#prefix + this.#accumulator.join(this.#delimiter) + this.#suffix; +// } +// } + +// export class SumCollectorInstance implements CollectorInstance { +// #accumulator = 0; + +// accumulate(element: number) { +// this.#accumulator += element; +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class BigIntSumCollectorInstance implements CollectorInstance { +// #accumulator = 0n; + +// accumulate(element: bigint) { +// this.#accumulator += element; +// } + +// finalize() { +// return this.#accumulator; +// } +// } + +// export class AverageCollectorInstance implements CollectorInstance { +// #count = 0; +// #sum = 0; + +// accumulate(element: number) { +// this.#count++; +// this.#sum += element; +// } + +// finalize() { +// return this.#count === 0 ? 0 : this.#sum / this.#count; +// } +// } + +// export class BigIntAverageCollectorInstance implements CollectorInstance { +// #count = 0n; +// #sum = 0n; + +// accumulate(element: bigint) { +// this.#count++; +// this.#sum += element; +// } + +// finalize() { +// return this.#count === 0n ? 0n : this.#sum / this.#count; +// } +// } diff --git a/src/collector/v2/index.ts b/src/collector/v2/index.ts new file mode 100644 index 0000000..6302661 --- /dev/null +++ b/src/collector/v2/index.ts @@ -0,0 +1,57 @@ +// import { Converter } from "../../types.js"; +// import { ToArrayCollectorInstance, ToObjectCollectorInstance, ToMapCollectorInstance, ToSetCollectorInstance, JoinCollectorInstance, SumCollectorInstance, BigIntSumCollectorInstance, AverageCollectorInstance, BigIntAverageCollectorInstance, ToArrayCollectorFactory } from "./impl.js"; +// import { CollectorFactory } from "./types.js"; + +// const toArrayCollectorFactory = new ToArrayCollectorFactory(); + +// export function toArray(): CollectorFactory { +// return toArrayCollectorFactory; +// } + +// export function toObject(keySelector: Converter, valueSelector: Converter): CollectorFactory> { +// return () => toObjectCollectorFactory(keySelector, valueSelector); +// } + +// export function toMap(keySelector: Converter, valueSelector: Converter): CollectorFactory> { +// return () => new ToMapCollectorInstance(keySelector, valueSelector); +// } + +// export function toSet(): CollectorFactory> { +// return () => new ToSetCollectorInstance(); +// } + +// export function join(delimiter?: string, prefix?: string, suffix?: string): CollectorFactory { +// return () => new JoinCollectorInstance(delimiter, prefix, suffix); +// } + +// function sumCollectorFactory() { +// return new SumCollectorInstance(); +// } + +// export function sum(): CollectorFactory { +// return sumCollectorFactory; +// } + +// function bigintSumCollectorFactory() { +// return new BigIntSumCollectorInstance(); +// } + +// export function bigintSum(): CollectorFactory { +// return bigintSumCollectorFactory; +// } + +// function averageCollectorFactory() { +// return new AverageCollectorInstance(); +// } + +// export function average(): CollectorFactory { +// return averageCollectorFactory; +// } + +// function bigintAverageCollectorFactory() { +// return new BigIntAverageCollectorInstance(); +// } + +// export function bigintAverage(): CollectorFactory { +// return bigintAverageCollectorFactory; +// } diff --git a/src/collector/v2/types.ts b/src/collector/v2/types.ts new file mode 100644 index 0000000..277ae61 --- /dev/null +++ b/src/collector/v2/types.ts @@ -0,0 +1,8 @@ +// export interface CollectorFactory { +// initialize(): CollectorInstance; +// } + +// export interface CollectorInstance { +// accumulate(element: TElement): void; +// finalize(): TResult; +// } diff --git a/src/comparer/async.ts b/src/comparer/async.ts new file mode 100644 index 0000000..ca9edfb --- /dev/null +++ b/src/comparer/async.ts @@ -0,0 +1,245 @@ +import { MaybeAsyncConverter } from "../types.js"; +import { Nullable } from "../utils.js"; +import { AsyncComparer, MaybeAsyncComparisonOrComparer, Comparer, MaybeAsyncComparison, AsyncComparison } from "./types.js"; + +export function isAsyncComparer(obj: any): obj is AsyncComparer { + return obj instanceof BaseAsyncComparer; +} + +export function asAsyncComparer(comparer: MaybeAsyncComparisonOrComparer): AsyncComparer { + return typeof comparer === "function" ? createAsyncComparer(comparer) : isAsyncComparer(comparer) ? comparer : new WrappedAsyncComparer(comparer); +} + +export function fromSync(comparer: Comparer): AsyncComparer { + return new WrappedAsyncComparer(comparer); +} + +export function createAsyncComparer(comparison: MaybeAsyncComparison): AsyncComparer { + return new SimpleAsyncComparer(comparison); +} + +export function createAsyncComparerUsing(projection: MaybeAsyncConverter, comparison?: MaybeAsyncComparisonOrComparer): AsyncComparer { + return new MappedAsyncComparer(projection, comparison); +} + +export function combineNullableAsyncComparers(comparers: Nullable>[]): AsyncComparer | undefined { + let result = defaultAsyncComparer; + + for (const comparer of comparers) { + if (!comparer) { + continue; + } + + result = result.then(asAsyncComparer(comparer)); + } + + return result === defaultAsyncComparer ? undefined : result; +} + +export abstract class BaseAsyncComparer implements AsyncComparer { + #cachedBoundComparison: AsyncComparison | undefined; + + public abstract compare(a: T, b: T): Promise; + + public comparison(): AsyncComparison { + return this.#cachedBoundComparison ??= this.compare.bind(this); + } + + public reverse(): AsyncComparer { + return new ReversedAsyncComparer(this); + } + + public then(comparer: AsyncComparer): AsyncComparer { + return new ThenAsyncComparer(this, comparer); + } + + public thenCompare(comparison: MaybeAsyncComparison): AsyncComparer { + return this.then(createAsyncComparer(comparison)); + } + + public thenCompareUsing(projection: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer): AsyncComparer { + return this.then(createAsyncComparerUsing(projection, comparer)); + } + + public nullAwareComparer(): AsyncComparer> { + return new NullAwareAsyncComparer(this); + } +} + +class WrappedAsyncComparer extends BaseAsyncComparer { + readonly #base: Comparer; + + constructor(base: Comparer) { + super(); + + this.#base = base; + } + + public override async compare(a: T, b: T) { + return this.#base.compare(a, b); + } +} + +class SimpleAsyncComparer extends BaseAsyncComparer { + readonly #comparison: MaybeAsyncComparison; + + public constructor(comparison: MaybeAsyncComparison) { + super(); + + this.#comparison = comparison; + } + + public async compare(a: T, b: T) { + return await this.#comparison(a, b); + } +} + +class MappedAsyncComparer extends BaseAsyncComparer { + readonly #projection: MaybeAsyncConverter; + readonly #comparer: AsyncComparer; + + public constructor(projection: MaybeAsyncConverter, comparer?: MaybeAsyncComparisonOrComparer) { + super(); + + this.#projection = projection; + this.#comparer = comparer ? asAsyncComparer(comparer) : defaultAsyncComparer; + } + + public async compare(a: T, b: T) { + return await this.#comparer.compare(await this.#projection(a), await this.#projection(b)); + } +} + +class ReversedAsyncComparer extends BaseAsyncComparer { + readonly #base: AsyncComparer; + + public constructor(base: AsyncComparer) { + super(); + + this.#base = base; + } + + public async compare(a: T, b: T) { + return await this.#base.compare(b, a); + } + + public override reverse() { + return this.#base; + } +} + +class ThenAsyncComparer extends BaseAsyncComparer { + readonly #base: AsyncComparer; + readonly #comparer: AsyncComparer; + + public constructor(base: AsyncComparer, comparer: AsyncComparer) { + super(); + + this.#base = base; + this.#comparer = comparer; + } + + public async compare(a: T, b: T) { + return await this.#base.compare(a, b) || await this.#comparer.compare(a, b); + } +} + +export const defaultAsyncComparer: AsyncComparer = new class DefaultAsyncComparer extends BaseAsyncComparer { + public override async compare(a: any, b: any): Promise { + if (a === undefined) { + if (b === undefined) { + return 0; + } + + return 1; + } + + if (b === undefined) { + return -1; + } + + const aStr = `${a}`, bStr = `${b}`; + + return aStr > bStr ? 1 : aStr < bStr ? -1 : 0; + } +}; + +export function getDefaultAsyncComparer(): AsyncComparer { + return defaultAsyncComparer; +} + +class NullAwareAsyncComparer extends BaseAsyncComparer> { + readonly #base: AsyncComparer; + + constructor(baseComparer: AsyncComparer) { + super(); + + this.#base = baseComparer; + } + + public override async compare(a: Nullable, b: Nullable): Promise { + if (a === undefined) { + if (b === undefined) { + return 0; + } + + return 1; + } + + if (b === undefined) { + return -1; + } + + if (a === null) { + if (b === null) { + return 0; + } + + return 1; + } + + if (b === null) { + return -1; + } + + return await this.#base.compare(a, b); + } + + public override nullAwareComparer(): AsyncComparer> { + return this; + } +} + +export function nullAwareComparer(baseComparer: AsyncComparer): AsyncComparer> { + return new NullAwareAsyncComparer(baseComparer); +} + +export const stringAsyncComparer: AsyncComparer = new class StringAsyncComparer extends BaseAsyncComparer { + public override async compare(a: string, b: string): Promise { + return a.localeCompare(b); + } +}; + +export const numberAsyncComparer: AsyncComparer = new class NumberAsyncComparer extends BaseAsyncComparer { + public override async compare(a: number, b: number): Promise { + return a - b; + } +}; + +export const bigintAsyncComparer: AsyncComparer = new class BigIntAsyncComparer extends BaseAsyncComparer { + public override async compare(a: bigint, b: bigint): Promise { + return Number(a - b); + } +}; + +export const booleanAsyncComparer: AsyncComparer = new class BooleanAsyncComparer extends BaseAsyncComparer { + public override async compare(a: boolean, b: boolean): Promise { + return Number(a) - Number(b); + } +}; + +export const dateAsyncComparer: AsyncComparer = new class DateAsyncComparer extends BaseAsyncComparer { + public override async compare(a: Date, b: Date): Promise { + return a.getTime() - b.getTime(); + } +}; diff --git a/src/comparer/sync.ts b/src/comparer/sync.ts new file mode 100644 index 0000000..dded405 --- /dev/null +++ b/src/comparer/sync.ts @@ -0,0 +1,244 @@ +import { Converter } from "../types.js"; +import { Nullable } from "../utils.js"; +import { fromSync } from "./async.js"; +import { Comparer, ComparisonOrComparer, Comparison, AsyncComparer } from "./types.js"; + +export function isComparer(obj: any): obj is Comparer { + return obj instanceof BaseComparer; +} + +export function asComparer(comparer: ComparisonOrComparer) { + return typeof comparer === "function" ? createComparer(comparer) : comparer; +} + +export function asComparison(comparer: ComparisonOrComparer) { + return typeof comparer === "function" ? comparer : comparer.comparison(); +} + +export function createComparer(comparison: Comparison): Comparer { + return new SimpleComparer(comparison); +} + +export function createComparerUsing(projection: Converter, comparison?: ComparisonOrComparer): Comparer { + return new MappedComparer(projection, comparison); +} + +export function reverseComparison(comparison: Comparison): Comparison { + return (a, b) => comparison(b, a); +} + +export function combineNullableComparers(comparers: Nullable>[]) { + let result = defaultComparer; + + for (const comparer of comparers) { + if (!comparer) { + continue; + } + + result = result.then(asComparer(comparer)); + } + + return result === defaultComparer ? undefined : result; +} + +export abstract class BaseComparer implements Comparer { + #cachedBoundComparison: Comparison | undefined; + + public abstract compare(a: T, b: T): number; + + public comparison(): Comparison { + return this.#cachedBoundComparison ??= this.compare.bind(this); + } + + public reverse(): Comparer { + return new ReversedComparer(this); + } + + public then(comparer: Comparer): Comparer { + return new ThenComparer(this, comparer); + } + + public thenCompare(comparison: Comparison): Comparer { + return this.then(createComparer(comparison)); + } + + public thenCompareUsing(projection: Converter, comparison?: ComparisonOrComparer): Comparer { + return this.then(createComparerUsing(projection, comparison)); + } + + public toAsync(): AsyncComparer { + return fromSync(this); + } + + public nullAwareComparer(): Comparer> { + return new NullAwareComparer(this); + } +} + +class SimpleComparer extends BaseComparer { + readonly #comparison: Comparison; + + public constructor(comparison: Comparison) { + super(); + + this.#comparison = comparison; + } + + public override compare(a: T, b: T) { + return this.#comparison(a, b); + } + + public override comparison() { + return this.#comparison; + } +} + +class MappedComparer extends BaseComparer { + readonly #projection: Converter; + readonly #comparison: Comparer; + + public constructor(projection: Converter, comparison?: ComparisonOrComparer) { + super(); + + this.#projection = projection; + this.#comparison = comparison ? asComparer(comparison) : defaultComparer; + } + + public override compare(a: T, b: T) { + return this.#comparison.compare(this.#projection(a), this.#projection(b)); + } +} + +class ReversedComparer extends BaseComparer { + readonly #base: Comparer; + + public constructor(base: Comparer) { + super(); + + this.#base = base; + } + + public override compare(a: T, b: T): number { + return this.#base.compare(b, a); + } + + public override reverse(): Comparer { + return this.#base; + } +} + +class ThenComparer extends BaseComparer { + readonly #base: Comparer; + readonly #comparer: Comparer; + + public constructor(base: Comparer, comparer: Comparer) { + super(); + + this.#base = base; + this.#comparer = comparer; + } + + public override compare(a: T, b: T) { + return this.#base.compare(a, b) || this.#comparer.compare(a, b); + } +} + +export const defaultComparer: Comparer = new class DefaultComparer extends BaseComparer { + public override compare(a: any, b: any): number { + if (a === undefined) { + if (b === undefined) { + return 0; + } + + return 1; + } + + if (b === undefined) { + return -1; + } + + const aStr = `${a}`, bStr = `${b}`; + + return aStr > bStr ? 1 : aStr < bStr ? -1 : 0; + } +}; + +export function getDefaultComparer(): Comparer { + return defaultComparer; +} + +class NullAwareComparer extends BaseComparer> { + readonly #base: Comparer; + + constructor(baseComparer: Comparer) { + super(); + + this.#base = baseComparer; + } + + public override compare(a: Nullable, b: Nullable): number { + if (a === undefined) { + if (b === undefined) { + return 0; + } + + return 1; + } + + if (b === undefined) { + return -1; + } + + if (a === null) { + if (b === null) { + return 0; + } + + return 1; + } + + if (b === null) { + return -1; + } + + return this.#base.compare(a, b); + } + + public override nullAwareComparer(): Comparer> { + return this; + } +} + +export function nullAwareComparer(baseComparer: Comparer): Comparer> { + return new NullAwareComparer(baseComparer); +} + +export const stringComparer: Comparer = new class StringComparer extends BaseComparer { + public override compare(a: string, b: string): number { + return a.localeCompare(b); + } +}; + +export const numberComparer: Comparer = new class NumberComparer extends BaseComparer { + public override compare(a: number, b: number): number { + return a - b; + } +}; + +export const bigintComparer: Comparer = new class BigIntComparer extends BaseComparer { + public override compare(a: bigint, b: bigint): number { + return Number(a - b); + } +}; + +export const booleanComparer: Comparer = new class BooleanComparer extends BaseComparer { + public override compare(a: boolean, b: boolean): number { + return Number(a) - Number(b); + } +}; + +export const dateComparer: Comparer = new class DateComparer extends BaseComparer { + public override compare(a: Date, b: Date): number { + return a.getTime() - b.getTime(); + } +}; diff --git a/src/comparer/types.ts b/src/comparer/types.ts new file mode 100644 index 0000000..84ba7ec --- /dev/null +++ b/src/comparer/types.ts @@ -0,0 +1,35 @@ +import { AsyncFunction, Converter, MaybeAsyncConverter, MaybeAsyncFunction } from "../types.js"; +import { Nullable } from "../utils.js"; + +export interface Comparer { + compare(a: T, b: T): number; + comparison(): Comparison; + reverse(): Comparer; + then(comparer: Comparer): Comparer; + thenCompare(comparison: Comparison): Comparer; + thenCompareUsing(projection: Converter, comparison?: ComparisonOrComparer): Comparer; + toAsync(): AsyncComparer; + nullAwareComparer(): Comparer>; +} + +export interface AsyncComparer { + compare(a: T, b: T): Promise; + comparison(): AsyncComparison; + reverse(): AsyncComparer; + then(comparer: AsyncComparer): AsyncComparer; + thenCompare(comparison: AsyncComparison): AsyncComparer; + thenCompareUsing(projection: MaybeAsyncConverter, comparison?: AsyncComparisonOrComparer): AsyncComparer; + nullAwareComparer(): AsyncComparer>; +} + +export type Comparison = (first: T, second: T) => number; +export type ComparisonOrComparer = Comparison | Comparer; +export type Equater = (first: T, second: T) => boolean; + +export type AsyncComparison = AsyncFunction>; +export type MaybeAsyncComparison = MaybeAsyncFunction>; +export type AsyncComparisonOrComparer = AsyncComparison | AsyncComparer; +export type MaybeAsyncComparisonOrComparer = MaybeAsyncComparison | Comparer | AsyncComparer; +export type AsyncEquater = AsyncFunction>; +export type MaybeAsyncEquater = MaybeAsyncFunction>; + diff --git a/src/equality-comparer/index.ts b/src/equality-comparer/index.ts new file mode 100644 index 0000000..f63be25 --- /dev/null +++ b/src/equality-comparer/index.ts @@ -0,0 +1,15 @@ +export function identity(obj: T) { + return obj; +} + +export function looseEquals(a: T, b: T) { + return a == b; +} + +export function strictEquals(a: T, b: T) { + return a === b; +} + +export function sameValue(a: T, b: T) { + return Object.is(a, b); +} diff --git a/src/equality-comparer/types.ts b/src/equality-comparer/types.ts new file mode 100644 index 0000000..9a11762 --- /dev/null +++ b/src/equality-comparer/types.ts @@ -0,0 +1,6 @@ +import { AsyncFunction, MaybeAsyncFunction } from "../types.js"; + +export type EqualityComparison = (first: T, second: T) => boolean; + +export type AsyncEqualityComparison = AsyncFunction>; +export type MaybeAsyncEqualityComparison = MaybeAsyncFunction>; diff --git a/src/equality-map.ts b/src/equality-map.ts index 9efc8bd..f1d0cec 100644 --- a/src/equality-map.ts +++ b/src/equality-map.ts @@ -1,15 +1,19 @@ -import { Equater, MaybeAsyncEquater } from "./types.js"; +import { EqualityComparison, MaybeAsyncEqualityComparison } from "./equality-comparer/types.js"; +import { MaybeAsyncIterable } from "./types.js"; -export interface EqualityMap extends Iterable<[K, V]> { +export type Entry = [key: K, value: V]; + +export interface EqualityMap extends Iterable> { readonly size: number; get(key: K): V | undefined; set(key: K, value: V): V | undefined; + setAll(entries: Iterable>): void; contains(key: K): boolean; remove(key: K): V | undefined; clear(): void; keys(): IterableIterator; values(): IterableIterator; - entries(): IterableIterator<[K, V]>; + entries(): IterableIterator>; } class NativeEqualityMap implements EqualityMap { @@ -29,6 +33,12 @@ class NativeEqualityMap implements EqualityMap { return existing; } + setAll(entries: Iterable>) { + for (const [key, value] of entries) { + this.set(key, value); + } + } + contains(key: K) { return this.#map.has(key); } @@ -61,10 +71,10 @@ class NativeEqualityMap implements EqualityMap { } class CustomEqualityMap implements EqualityMap { - readonly #list: [K, V][] = []; - readonly #keyComparer: Equater; + readonly #list: Entry[] = []; + readonly #keyComparer: EqualityComparison; - constructor(keyComparer: Equater) { + constructor(keyComparer: EqualityComparison) { this.#keyComparer = keyComparer; } @@ -96,6 +106,12 @@ class CustomEqualityMap implements EqualityMap { return undefined; } + setAll(entries: Iterable>) { + for (const [key, value] of entries) { + this.set(key, value); + } + } + contains(key: K) { for (const entry of this.#list) { if (this.#keyComparer(key, entry[0])) { @@ -139,25 +155,26 @@ class CustomEqualityMap implements EqualityMap { *[Symbol.iterator]() { for (const entry of this.#list) { - yield entry.slice() as [K, V]; // no entry mutation allowed! + yield entry.slice() as Entry; // no entry mutation allowed! } } } -export function createEqualityMap(keyComparer?: Equater): EqualityMap { +export function createEqualityMap(keyComparer?: EqualityComparison): EqualityMap { return keyComparer ? new CustomEqualityMap(keyComparer) : new NativeEqualityMap(); } -export interface AsyncEqualityMap extends Iterable<[K, V]> { +export interface AsyncEqualityMap extends Iterable> { readonly size: number; get(key: K): Promise; set(key: K, value: V): Promise; + setAll(entries: MaybeAsyncIterable>): Promise; contains(key: K): Promise; remove(key: K): Promise; clear(): void; keys(): IterableIterator; values(): IterableIterator; - entries(): IterableIterator<[K, V]>; + entries(): IterableIterator>; } class NativeAsyncEqualityMap implements AsyncEqualityMap { @@ -177,6 +194,12 @@ class NativeAsyncEqualityMap implements AsyncEqualityMap { return existing; } + async setAll(entries: MaybeAsyncIterable>) { + for await (const [key, value] of entries) { + await this.set(key, value); + } + } + async contains(key: K) { return this.#map.has(key); } @@ -209,10 +232,10 @@ class NativeAsyncEqualityMap implements AsyncEqualityMap { } class CustomAsyncEqualityMap implements AsyncEqualityMap { - readonly #list: [K, V][] = []; - readonly #keyComparer: MaybeAsyncEquater; + readonly #list: Entry[] = []; + readonly #keyComparer: MaybeAsyncEqualityComparison; - constructor(keyComparer: MaybeAsyncEquater) { + constructor(keyComparer: MaybeAsyncEqualityComparison) { this.#keyComparer = keyComparer; } @@ -244,6 +267,12 @@ class CustomAsyncEqualityMap implements AsyncEqualityMap { return undefined; } + async setAll(entries: MaybeAsyncIterable>) { + for await (const [key, value] of entries) { + await this.set(key, value); + } + } + async contains(key: K) { for (const entry of this.#list) { if (await this.#keyComparer(key, entry[0])) { @@ -287,11 +316,11 @@ class CustomAsyncEqualityMap implements AsyncEqualityMap { *[Symbol.iterator]() { for (const entry of this.#list) { - yield entry.slice() as [K, V]; // no entry mutation allowed! + yield entry.slice() as Entry; // no entry mutation allowed! } } } -export function createAsyncEqualityMap(keyComparer?: MaybeAsyncEquater): AsyncEqualityMap { +export function createAsyncEqualityMap(keyComparer?: MaybeAsyncEqualityComparison): AsyncEqualityMap { return keyComparer ? new CustomAsyncEqualityMap(keyComparer) : new NativeAsyncEqualityMap(); } diff --git a/src/equality-set.ts b/src/equality-set.ts index 3660898..6c08cdd 100644 --- a/src/equality-set.ts +++ b/src/equality-set.ts @@ -1,10 +1,12 @@ -import { Equater, MaybeAsyncEquater } from "./types.js"; +import { EqualityComparison, MaybeAsyncEqualityComparison } from "./equality-comparer/types.js"; +import { MaybeAsyncIterable } from "./types.js"; export interface EqualitySet extends Iterable { readonly size: number; - add(obj: T): boolean; - contains(obj: T): boolean; - remove(obj: T): boolean; + add(value: T): boolean; + addAll(values: Iterable): number; + contains(value: T): boolean; + remove(value: T): boolean; clear(): void; values(): IterableIterator; } @@ -16,18 +18,30 @@ class NativeEqualitySet implements EqualitySet { return this.#set.size; } - add(obj: T) { - const exists = this.contains(obj); - this.#set.add(obj); + add(value: T) { + const exists = this.contains(value); + this.#set.add(value); return !exists; } - contains(obj: T) { - return this.#set.has(obj); + addAll(values: Iterable) { + let result = 0; + + for (const value of values) { + if (this.add(value)) { + result++; + } + } + + return result; } - remove(obj: T) { - return this.#set.delete(obj); + contains(value: T) { + return this.#set.has(value); + } + + remove(value: T) { + return this.#set.delete(value); } clear() { @@ -45,9 +59,9 @@ class NativeEqualitySet implements EqualitySet { class CustomEqualitySet implements EqualitySet { readonly #list: T[] = []; - readonly #equater: Equater; + readonly #equater: EqualityComparison; - constructor(equater: Equater) { + constructor(equater: EqualityComparison) { this.#equater = equater; } @@ -55,19 +69,31 @@ class CustomEqualitySet implements EqualitySet { return this.#list.length; } - add(obj: T) { - if (this.contains(obj)) { + add(value: T) { + if (this.contains(value)) { return false; } - this.#list.push(obj); + this.#list.push(value); return true; } - contains(obj: T) { + addAll(values: Iterable) { + let result = 0; + + for (const value of values) { + if (this.add(value)) { + result++; + } + } + + return result; + } + + contains(value: T) { for (const val of this.#list) { - if (this.#equater(obj, val)) { + if (this.#equater(value, val)) { return true; } } @@ -75,11 +101,11 @@ class CustomEqualitySet implements EqualitySet { return false; } - remove(obj: T) { + remove(value: T) { const length = this.#list.length; for (let i = 0; i < length; i++) { - if (this.#equater(obj, this.#list[i])) { + if (this.#equater(value, this.#list[i])) { this.#list.splice(i, 1); return true; } @@ -101,15 +127,16 @@ class CustomEqualitySet implements EqualitySet { } } -export function createEqualitySet(equater?: Equater): EqualitySet { +export function createEqualitySet(equater?: EqualityComparison): EqualitySet { return equater ? new CustomEqualitySet(equater) : new NativeEqualitySet(); } export interface AsyncEqualitySet extends Iterable { readonly size: number; - add(obj: T): Promise; - contains(obj: T): Promise; - remove(obj: T): Promise; + add(value: T): Promise; + addAll(values: MaybeAsyncIterable): Promise; + contains(value: T): Promise; + remove(value: T): Promise; clear(): void; values(): IterableIterator; } @@ -121,18 +148,30 @@ class NativeAsyncEqualitySet implements AsyncEqualitySet { return this.#set.size; } - async add(obj: T) { - const exists = await this.contains(obj); - this.#set.add(obj); + async add(value: T) { + const exists = await this.contains(value); + this.#set.add(value); return !exists; } - async contains(obj: T) { - return this.#set.has(obj); + async addAll(values: MaybeAsyncIterable) { + let result = 0; + + for await (const value of values) { + if (await this.add(value)) { + result++; + } + } + + return result; } - async remove(obj: T) { - return this.#set.delete(obj); + async contains(value: T) { + return this.#set.has(value); + } + + async remove(value: T) { + return this.#set.delete(value); } clear() { @@ -150,9 +189,9 @@ class NativeAsyncEqualitySet implements AsyncEqualitySet { class CustomAsyncEqualitySet implements AsyncEqualitySet { readonly #list: T[] = []; - readonly #equater: MaybeAsyncEquater; + readonly #equater: MaybeAsyncEqualityComparison; - constructor(equater: MaybeAsyncEquater) { + constructor(equater: MaybeAsyncEqualityComparison) { this.#equater = equater; } @@ -160,19 +199,31 @@ class CustomAsyncEqualitySet implements AsyncEqualitySet { return this.#list.length; } - async add(obj: T) { - if (await this.contains(obj)) { + async add(value: T) { + if (await this.contains(value)) { return false; } - this.#list.push(obj); + this.#list.push(value); return true; } - async contains(obj: T) { + async addAll(values: MaybeAsyncIterable) { + let result = 0; + + for await (const value of values) { + if (await this.add(value)) { + result++; + } + } + + return result; + } + + async contains(value: T) { for (const val of this.#list) { - if (await this.#equater(obj, val)) { + if (await this.#equater(value, val)) { return true; } } @@ -180,11 +231,11 @@ class CustomAsyncEqualitySet implements AsyncEqualitySet { return false; } - async remove(obj: T) { + async remove(value: T) { const length = this.#list.length; for (let i = 0; i < length; i++) { - if (await this.#equater(obj, this.#list[i])) { + if (await this.#equater(value, this.#list[i])) { this.#list.splice(i, 1); return true; } @@ -206,6 +257,6 @@ class CustomAsyncEqualitySet implements AsyncEqualitySet { } } -export function createAsyncEqualitySet(equater?: MaybeAsyncEquater): AsyncEqualitySet { +export function createAsyncEqualitySet(equater?: MaybeAsyncEqualityComparison): AsyncEqualitySet { return equater ? new CustomAsyncEqualitySet(equater) : new NativeAsyncEqualitySet(); } diff --git a/src/index.ts b/src/index.ts index 38dd087..d0842cf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ -export { BaseSequence, DelegatedSequence } from "./sync/impl.js"; +export { BaseSequence } from "./sync/impl.js"; export * as Sequences from "./sync/index.js"; export * from "./sync/types.js"; -export { BaseAsyncSequence, DelegatedAsyncSequence } from "./async/impl.js"; +export { BaseAsyncSequence } from "./async/impl.js"; export * as AsyncSequences from "./async/index.js"; export * from "./async/types.js"; export * as Collectors from "./collector/index.js"; @@ -10,3 +10,10 @@ export * as BitArrays from "./bitarray/index.js"; export * from "./bitarray/types.js"; export * as Random from "./random/index.js"; export * from "./random/types.js"; +export { BaseComparer } from "./comparer/sync.js"; +export * as Comparers from "./comparer/sync.js"; +export { BaseAsyncComparer } from "./comparer/async.js"; +export * as AsyncComparers from "./comparer/async.js"; +export * from "./comparer/types.js"; +export * as EqualityComparers from "./equality-comparer/index.js"; +export * from "./equality-comparer/types.js"; diff --git a/src/sorting.ts b/src/sorting.ts index 1147b81..b855d5b 100644 --- a/src/sorting.ts +++ b/src/sorting.ts @@ -1,8 +1,8 @@ -import { MaybeAsyncComparer } from "./types.js"; -import { reverseAsyncComparer } from "./utils.js"; +import { asAsyncComparer } from "./comparer/async.js"; +import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "./comparer/types.js"; export interface AsyncSorter { - sort(array: T[], descending: boolean, comparer: MaybeAsyncComparer): Promise; + sort(array: T[], descending: boolean, comparer: MaybeAsyncComparisonOrComparer): Promise; } function swap(array: any[], i: number, j: number) { @@ -12,20 +12,26 @@ function swap(array: any[], i: number, j: number) { } abstract class BaseAsyncSorter implements AsyncSorter { - async sort(array: T[], descending: boolean, comparer: MaybeAsyncComparer) { - await this._sort(array, descending ? reverseAsyncComparer(comparer) : comparer); + async sort(array: T[], descending: boolean, comparer: MaybeAsyncComparisonOrComparer) { + comparer = asAsyncComparer(comparer); + + if (descending) { + comparer = comparer.reverse(); + } + + await this._sort(array, comparer); } - protected abstract _sort(array: T[], comparer: MaybeAsyncComparer): Promise; + protected abstract _sort(array: T[], comparer: AsyncComparer): Promise; } -class InsertionSorter extends BaseAsyncSorter { - protected override async _sort(array: T[], comparer: MaybeAsyncComparer) { +export const insertionSorter: AsyncSorter = new class InsertionSorter extends BaseAsyncSorter { + protected override async _sort(array: T[], comparer: AsyncComparer) { for (let i = 1; i < array.length; i++) { const obj = array[i]; for (let j = i - 1; j >= 0; j--) { - if (await comparer(obj, array[j]) < 0) { + if (await comparer.compare(obj, array[j]) < 0) { for (let k = i; k > j; k--) { swap(array, k - 1, k); } @@ -35,10 +41,10 @@ class InsertionSorter extends BaseAsyncSorter { } } } -} +}; -class SelectionSorter extends BaseAsyncSorter { - protected async _sort(array: T[], comparer: MaybeAsyncComparer) { +export const selectionSorter: AsyncSorter = new class SelectionSorter extends BaseAsyncSorter { + protected async _sort(array: T[], comparer: AsyncComparer) { for (let i = 0; i < array.length; i++) { let smallest = array[i]; let smallestIndex = i; @@ -46,7 +52,7 @@ class SelectionSorter extends BaseAsyncSorter { for (let j = i; j < array.length; j++) { const current = array[j]; - if (await comparer(current, smallest) < 0) { + if (await comparer.compare(current, smallest) < 0) { smallest = current; smallestIndex = j; } @@ -57,10 +63,10 @@ class SelectionSorter extends BaseAsyncSorter { } } } -} +}; -class BubbleSorter extends BaseAsyncSorter { - protected async _sort(array: T[], comparer: MaybeAsyncComparer) { +export const bubbleSorter: AsyncSorter = new class BubbleSorter extends BaseAsyncSorter { + protected async _sort(array: T[], comparer: AsyncComparer) { const length = array.length; for (let k = length - 2; k >= 0; k--) { @@ -70,7 +76,7 @@ class BubbleSorter extends BaseAsyncSorter { const j = i + 1; const a = array[i], b = array[j]; - if (await comparer(a, b) > 0) { + if (await comparer.compare(a, b) > 0) { swap(array, i, j); hasSwaped = true; } @@ -81,8 +87,4 @@ class BubbleSorter extends BaseAsyncSorter { } } } -} - -export const insertionSorter: AsyncSorter = new InsertionSorter(); -export const selectionSorter: AsyncSorter = new SelectionSorter(); -export const bubbleSorter: AsyncSorter = new BubbleSorter(); +}; diff --git a/src/sync/impl.ts b/src/sync/impl.ts index 91ada2e..e861f1d 100644 --- a/src/sync/impl.ts +++ b/src/sync/impl.ts @@ -1,14 +1,17 @@ import { BaseAsyncSequence } from "../async/impl.js"; import { AsyncSequence } from "../async/types.js"; import { Collector } from "../collector/types.js"; +import { asComparer, combineNullableComparers, createComparerUsing, defaultComparer } from "../comparer/sync.js"; +import { ComparisonOrComparer, Comparer } from "../comparer/types.js"; +import { strictEquals, identity } from "../equality-comparer/index.js"; +import { EqualityComparison } from "../equality-comparer/types.js"; import { createEqualityMap } from "../equality-map.js"; import { createEqualitySet } from "../equality-set.js"; import { createQueue } from "../queue.js"; import { getRandomElement } from "../random/index.js"; import { RandomOptions } from "../random/types.js"; -import { AnyPredicate, Converter, FilterPredicate, Equater, BiConverter, Accumulator, Comparer, Action } from "../types.js"; -import { strictEquals, identity, operatorCompare, reverseComparer, asIterable, defaultArrayComparer, combineComparers } from "../utils.js"; -import { array, empty } from "./index.js"; +import { AnyPredicate, Converter, TypePredicate, BiConverter, Accumulator, Action } from "../types.js"; +import { array, empty, wrap } from "./index.js"; import { Sequence, GroupedSequence, OrderedSequence, SequencePipeline } from "./types.js"; export class SequenceMarker { } @@ -36,29 +39,29 @@ export abstract class BaseSequence extends SequenceMarker implements S return new SelectSequence(this, converter); } - selectMany(converter: Converter>): Sequence { + selectMany(converter: Converter>): Sequence { return new SelectManySequence(this, converter); } - where(predicate: FilterPredicate): Sequence; + where(predicate: TypePredicate): Sequence; where(predicate: AnyPredicate): Sequence; where(predicate: any) { return new WhereSequence(this, predicate); } - groupBy(keySelector: Converter, elementSelector?: Converter, keyComparer?: Equater): Sequence> { + groupBy(keySelector: Converter, elementSelector?: Converter, keyComparer?: EqualityComparison): Sequence> { return new GroupBySequence(this, keySelector, elementSelector, keyComparer); } - join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: Equater): Sequence { - return new JoinSequence(this, sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); + join(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyComparer?: EqualityComparison): Sequence { + return new JoinSequence(this, wrap(sequence), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: Equater): Sequence { - return new GroupJoinSequence(this, sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); + groupJoin(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyComparer?: EqualityComparison): Sequence { + return new GroupJoinSequence(this, wrap(sequence), firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - contains(obj: TElement, equater?: Equater) { + contains(obj: TElement, equater?: EqualityComparison) { if (!equater) { equater = strictEquals; } @@ -72,13 +75,15 @@ export abstract class BaseSequence extends SequenceMarker implements S return false; } - sequenceEquals(sequence: Sequence, equater?: Equater) { + sequenceEquals(sequence: Iterable, equater?: EqualityComparison) { if (this === sequence) { return true; } + const other = wrap(sequence); + const thisCount = this.nonEnumeratedCount(); - const thatCount = sequence.nonEnumeratedCount(); + const thatCount = other.nonEnumeratedCount(); if (thisCount >= 0 && thatCount >= 0 && thisCount !== thatCount) { return false; @@ -89,7 +94,7 @@ export abstract class BaseSequence extends SequenceMarker implements S } const thisIterator = this.iterator(); - const thatIterator = sequence.iterator(); + const thatIterator = other.iterator(); while (true) { const thisNext = thisIterator.next(); @@ -117,11 +122,11 @@ export abstract class BaseSequence extends SequenceMarker implements S return new PrependSequence(this, obj); } - remove(obj: TElement, all?: boolean, equater?: Equater): Sequence { + remove(obj: TElement, all?: boolean, equater?: EqualityComparison): Sequence { return new RemoveSequence(this, obj, all, equater); } - concat(...sequences: Sequence[]) { + concat(...sequences: Iterable[]) { if (sequences.length === 0) { return this; } @@ -129,7 +134,7 @@ export abstract class BaseSequence extends SequenceMarker implements S const arr: Sequence[] = [this]; for (const sequence of sequences) { - arr.push(sequence); + arr.push(wrap(sequence)); } return new ConcatSequence(arr); @@ -400,7 +405,7 @@ export abstract class BaseSequence extends SequenceMarker implements S return acc as unknown as TResult; } - #find(sorter: AnyPredicate, selector?: Converter, comparer?: Comparer) { + #find(sorter: AnyPredicate, selector?: Converter, comparer?: ComparisonOrComparer) { const iterator = this.iterator(); let next = iterator.next(); @@ -413,9 +418,7 @@ export abstract class BaseSequence extends SequenceMarker implements S selector = identity as Converter; } - if (!comparer) { - comparer = operatorCompare; - } + comparer = comparer ? asComparer(comparer) : defaultComparer; let result = next.value; let convertedResult = selector(result); @@ -430,7 +433,7 @@ export abstract class BaseSequence extends SequenceMarker implements S const value = next.value; const convertedValue = selector(value); - if (sorter(comparer(convertedResult, convertedValue))) { + if (sorter(comparer.compare(convertedResult, convertedValue))) { result = value; convertedResult = convertedValue; } @@ -439,23 +442,23 @@ export abstract class BaseSequence extends SequenceMarker implements S return result; } - min(comparer?: Comparer) { + min(comparer?: ComparisonOrComparer) { return this.#find(x => x > 0, undefined, comparer); } - minBy(converter: Converter, comparer?: Comparer) { + minBy(converter: Converter, comparer?: ComparisonOrComparer) { return this.#find(x => x > 0, converter, comparer); } - max(comparer?: Comparer) { + max(comparer?: ComparisonOrComparer) { return this.#find(x => x < 0, undefined, comparer); } - maxBy(converter: Converter, comparer?: Comparer) { + maxBy(converter: Converter, comparer?: ComparisonOrComparer) { return this.#find(x => x < 0, converter, comparer); } - #findBounds(selector?: Converter, comparer?: Comparer) { + #findBounds(selector?: Converter, comparer?: ComparisonOrComparer): [min: TElement, max: TElement] { const iterator = this.iterator(); let next = iterator.next(); @@ -468,9 +471,7 @@ export abstract class BaseSequence extends SequenceMarker implements S selector = identity as Converter; } - if (!comparer) { - comparer = operatorCompare; - } + comparer = comparer ? asComparer(comparer) : defaultComparer; let minBound = next.value, maxBound = minBound; let convertedMinBound = selector(minBound), convertedMaxBound = convertedMinBound; @@ -485,82 +486,82 @@ export abstract class BaseSequence extends SequenceMarker implements S const value = next.value; const convertedValue = selector(value); - if (comparer(convertedMinBound, convertedValue) > 0) { + if (comparer.compare(convertedMinBound, convertedValue) > 0) { minBound = value; convertedMinBound = convertedValue; } - if (comparer(convertedMaxBound, convertedValue) < 0) { + if (comparer.compare(convertedMaxBound, convertedValue) < 0) { maxBound = value; convertedMaxBound = convertedValue; } } - return { min: minBound, max: maxBound }; + return [minBound, maxBound]; } - bounds(comparer?: Comparer) { + bounds(comparer?: ComparisonOrComparer) { return this.#findBounds(undefined, comparer); } - boundsBy(converter: Converter, comparer?: Comparer) { + boundsBy(converter: Converter, comparer?: ComparisonOrComparer) { return this.#findBounds(converter, comparer); } - order(comparer?: Comparer): OrderedSequence { + order(comparer?: ComparisonOrComparer): OrderedSequence { return new OrderSequence(this, false, comparer); } - orderBy(selector: Converter, comparer?: Comparer): OrderedSequence { + orderBy(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence { return new OrderBySequence(this, false, selector, comparer); } - orderDescending(comparer?: Comparer): OrderedSequence { + orderDescending(comparer?: ComparisonOrComparer): OrderedSequence { return new OrderSequence(this, true, comparer); } - orderByDescending(selector: Converter, comparer?: Comparer): OrderedSequence { + orderByDescending(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence { return new OrderBySequence(this, true, selector, comparer); } - partition(equater?: Equater): Sequence> { + partition(equater?: EqualityComparison): Sequence> { return new PartitionSequence(this, equater); } - partitionBy(selector: Converter, equater?: Equater): Sequence> { + partitionBy(selector: Converter, equater?: EqualityComparison): Sequence> { return new PartitionBySequence(this, selector, equater); } - distinct(equater?: Equater): Sequence { + distinct(equater?: EqualityComparison): Sequence { return new DistinctSequence(this, equater); } - distinctBy(selector: Converter, equater?: Equater): Sequence { + distinctBy(selector: Converter, equater?: EqualityComparison): Sequence { return new DistinctBySequence(this, selector, equater); } - union(sequence: Sequence, equater?: Equater): Sequence { - return new UnionSequence(this, sequence, equater); + union(sequence: Iterable, equater?: EqualityComparison): Sequence { + return new UnionSequence(this, wrap(sequence), equater); } - unionBy(sequence: Sequence, selector: Converter, equater?: Equater): Sequence { - return new UnionBySequence(this, sequence, selector, equater); + unionBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison): Sequence { + return new UnionBySequence(this, wrap(sequence), selector, equater); } - except(sequence: Sequence): Sequence { - return new ExceptSequence(this, sequence); + except(sequence: Iterable): Sequence { + return new ExceptSequence(this, wrap(sequence)); } - exceptBy(sequence: Sequence, selector: Converter): Sequence { - return new ExceptBySequence(this, sequence, selector); + exceptBy(sequence: Iterable, selector: Converter): Sequence { + return new ExceptBySequence(this, wrap(sequence), selector); } - intersect(sequence: Sequence): Sequence { - return new IntersectSequence(this, sequence); + intersect(sequence: Iterable): Sequence { + return new IntersectSequence(this, wrap(sequence)); } - intersectBy(sequence: Sequence, selector: Converter): Sequence { - return new IntersectBySequence(this, sequence, selector); + intersectBy(sequence: Iterable, selector: Converter): Sequence { + return new IntersectBySequence(this, wrap(sequence), selector); } all(predicate: AnyPredicate) { @@ -669,8 +670,8 @@ export abstract class BaseSequence extends SequenceMarker implements S } } - zip(sequence: Sequence): Sequence<[TElement, TOther]> { - return new ZippedSequence(this, sequence); + zip(sequence: Iterable): Sequence<[TElement, TOther]> { + return new ZippedSequence(this, wrap(sequence)); } indexed(): Sequence<[number, TElement]> { @@ -682,14 +683,19 @@ export abstract class BaseSequence extends SequenceMarker implements S } chunked(size: number): Sequence>; + chunked(size: number, asArray: true): Sequence; chunked(size: number, transformer: SequencePipeline): Sequence; - chunked(size: number, transformer?: SequencePipeline): Sequence> | Sequence { + chunked(size: number, transformerOrAsArray?: true | SequencePipeline): Sequence> | Sequence | Sequence { if (size <= 0) { throw new Error("Chunk size must be positive."); } + if (transformerOrAsArray === true) { + return new ChunkedArraySequence(this, size); + } + const result = new ChunkedSequence(this, size); - return transformer ? result.select(transformer) : result; + return transformerOrAsArray ? result.select(transformerOrAsArray) : result; } random(options?: RandomOptions) { @@ -814,39 +820,39 @@ export class DelegatedSequence extends SequenceMarker implements Seque return this.#sequence.select(selector); } - selectMany(selector: Converter>) { + selectMany(selector: Converter>) { return this.#sequence.selectMany(selector); } - where(predicate: FilterPredicate): Sequence; + where(predicate: TypePredicate): Sequence; where(predicate: AnyPredicate): Sequence; where(predicate: any): Sequence { return this.#sequence.where(predicate); } - groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: Equater | undefined): Sequence>; - groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: Equater | undefined): Sequence>; + groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: EqualityComparison | undefined): Sequence>; + groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: EqualityComparison | undefined): Sequence>; groupBy(keySelector: any, elementSelector?: any, keyComparer?: any) { return this.#sequence.groupBy(keySelector, elementSelector, keyComparer); } - join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater | undefined): Sequence<[TElement, TOther]>; - join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: Equater | undefined): Sequence; + join(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: EqualityComparison | undefined): Sequence<[TElement, TOther]>; + join(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: EqualityComparison | undefined): Sequence; join(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.join(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater | undefined): Sequence>; - groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: Equater | undefined): Sequence; + groupJoin(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: EqualityComparison | undefined): Sequence>; + groupJoin(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: EqualityComparison | undefined): Sequence; groupJoin(sequence: any, firstKeySelector: any, secondKeySelector: any, resultSelector?: any, keyComparer?: any) { return this.#sequence.groupJoin(sequence, firstKeySelector, secondKeySelector, resultSelector, keyComparer); } - contains(obj: TElement, equater?: Equater) { + contains(obj: TElement, equater?: EqualityComparison) { return this.#sequence.contains(obj, equater); } - sequenceEquals(sequence: Sequence, equater?: Equater) { + sequenceEquals(sequence: Iterable, equater?: EqualityComparison) { return this.#sequence.sequenceEquals(sequence, equater); } @@ -858,11 +864,11 @@ export class DelegatedSequence extends SequenceMarker implements Seque return this.#sequence.prepend(obj); } - remove(obj: TElement, all?: boolean, equater?: Equater) { + remove(obj: TElement, all?: boolean, equater?: EqualityComparison) { return this.#sequence.remove(obj, all, equater); } - concat(...sequences: Sequence[]) { + concat(...sequences: Iterable[]) { return this.#sequence.concat(...sequences); } @@ -902,83 +908,83 @@ export class DelegatedSequence extends SequenceMarker implements Seque return this.#sequence.aggregate(accumulator, seed, resultSelector); } - min(comparer?: Comparer) { + min(comparer?: ComparisonOrComparer) { return this.#sequence.min(comparer); } - minBy(selector: Converter, comparer?: Comparer) { + minBy(selector: Converter, comparer?: ComparisonOrComparer) { return this.#sequence.minBy(selector, comparer); } - max(comparer?: Comparer) { + max(comparer?: ComparisonOrComparer) { return this.#sequence.max(comparer); } - maxBy(selector: Converter, comparer?: Comparer) { + maxBy(selector: Converter, comparer?: ComparisonOrComparer) { return this.#sequence.maxBy(selector, comparer); } - bounds(comparer?: Comparer) { + bounds(comparer?: ComparisonOrComparer) { return this.#sequence.bounds(comparer); } - boundsBy(selector: Converter, comparer?: Comparer) { + boundsBy(selector: Converter, comparer?: ComparisonOrComparer) { return this.#sequence.boundsBy(selector, comparer); } - order(comparer?: Comparer) { + order(comparer?: ComparisonOrComparer) { return this.#sequence.order(comparer); } - orderBy(selector: Converter, comparer?: Comparer) { + orderBy(selector: Converter, comparer?: ComparisonOrComparer) { return this.#sequence.orderBy(selector, comparer); } - orderDescending(comparer?: Comparer) { + orderDescending(comparer?: ComparisonOrComparer) { return this.#sequence.orderDescending(comparer); } - orderByDescending(selector: Converter, comparer?: Comparer) { + orderByDescending(selector: Converter, comparer?: ComparisonOrComparer) { return this.#sequence.orderByDescending(selector, comparer); } - partition(equater?: Equater | undefined): Sequence> { + partition(equater?: EqualityComparison | undefined): Sequence> { return this.#sequence.partition(equater); } - partitionBy(selector: Converter, equater?: Equater | undefined): Sequence> { + partitionBy(selector: Converter, equater?: EqualityComparison | undefined): Sequence> { return this.#sequence.partitionBy(selector, equater); } - distinct(equater?: Equater) { + distinct(equater?: EqualityComparison) { return this.#sequence.distinct(equater); } - distinctBy(selector: Converter, equater?: Equater) { + distinctBy(selector: Converter, equater?: EqualityComparison) { return this.#sequence.distinctBy(selector, equater); } - union(sequence: Sequence, equater?: Equater) { + union(sequence: Iterable, equater?: EqualityComparison) { return this.#sequence.union(sequence, equater); } - unionBy(sequence: Sequence, selector: Converter, equater?: Equater) { + unionBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison) { return this.#sequence.unionBy(sequence, selector, equater); } - except(sequence: Sequence, equater?: Equater) { + except(sequence: Iterable, equater?: EqualityComparison) { return this.#sequence.except(sequence, equater); } - exceptBy(sequence: Sequence, selector: Converter, equater?: Equater) { + exceptBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison) { return this.#sequence.exceptBy(sequence, selector, equater); } - intersect(sequence: Sequence, equater?: Equater) { + intersect(sequence: Iterable, equater?: EqualityComparison) { return this.#sequence.intersect(sequence, equater); } - intersectBy(sequence: Sequence, selector: Converter, equater?: Equater) { + intersectBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison) { return this.#sequence.intersectBy(sequence, selector, equater); } @@ -1030,7 +1036,7 @@ export class DelegatedSequence extends SequenceMarker implements Seque this.#sequence.forEach(action); } - zip(sequence: Sequence) { + zip(sequence: Iterable) { return this.#sequence.zip(sequence); } @@ -1043,9 +1049,10 @@ export class DelegatedSequence extends SequenceMarker implements Seque } chunked(size: number): Sequence>; + chunked(size: number, asArray: true): Sequence; chunked(size: number, transformer: SequencePipeline): Sequence; - chunked(size: number, transformer?: any): any { - return this.#sequence.chunked(size, transformer); + chunked(size: number, transformerOrAsArray?: any): any { + return this.#sequence.chunked(size, transformerOrAsArray); } random(options?: RandomOptions) { @@ -1107,17 +1114,16 @@ export class GroupedSequenceImpl extends DelegatedSequence extends BaseSequence implements OrderedSequence { readonly #sequence: Sequence; - readonly #sorter: Comparer | undefined; + readonly #sorter: Comparer; readonly #descending: boolean; - constructor(sequence: Sequence, sorter: Comparer | undefined, descending: boolean) { + constructor(sequence: Sequence, sorter: ComparisonOrComparer | undefined, descending: boolean) { super(); this.#sequence = sequence; - this.#sorter = sorter; + this.#sorter = sorter ? asComparer(sorter) : defaultComparer; this.#descending = descending; } @@ -1141,19 +1147,19 @@ export abstract class BaseOrderedSequence extends BaseSequence): OrderedSequence { + thenSelf(comparer?: ComparisonOrComparer): OrderedSequence { return new ThenOrderSequence(this, false, comparer); } - thenBy(selector: Converter, comparer?: Comparer): OrderedSequence { + thenBy(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence { return new ThenOrderBySequence(this, false, selector, comparer); } - thenSelfDescending(comparer?: Comparer): OrderedSequence { + thenSelfDescending(comparer?: ComparisonOrComparer): OrderedSequence { return new ThenOrderSequence(this, true, comparer); } - thenByDescending(selector: Converter, comparer?: Comparer): OrderedSequence { + thenByDescending(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence { return new ThenOrderBySequence(this, true, selector, comparer); } @@ -1165,7 +1171,7 @@ export abstract class BaseOrderedSequence extends BaseSequence extends BaseSequence { return this.#array.length; } - override contains(obj: T, equater?: Equater): boolean { + override _countWithoutPredicate() { + return this.#array.length; + } + + override contains(obj: T, equater?: EqualityComparison): boolean { if (equater) { return this.#array.some(x => equater(x, obj)); } @@ -1457,7 +1467,7 @@ export class WrappedSet extends BaseSequence { return this.#set.size; } - override contains(obj: T, equater?: Equater) { + override contains(obj: T, equater?: EqualityComparison) { if (equater) { return super.contains(obj, equater); } @@ -1487,7 +1497,7 @@ export class WrappedMap extends BaseSequence<[K, V]> { return this.#map.size; } - override contains(obj: [K, V], equater?: Equater<[K, V]>) { + override contains(obj: [K, V], equater?: EqualityComparison<[K, V]>) { if (equater) { return super.contains(obj, equater); } @@ -1596,9 +1606,9 @@ export class ConcatSequence extends BaseSequence { class DistinctSequence extends BaseSequence { readonly #sequence: Sequence; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(sequence: Sequence, equater?: Equater) { + constructor(sequence: Sequence, equater?: EqualityComparison) { super(); this.#sequence = sequence; @@ -1623,9 +1633,9 @@ class DistinctSequence extends BaseSequence { class DistinctBySequence extends BaseSequence { readonly #sequence: Sequence; readonly #converter: Converter; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(sequence: Sequence, converter: Converter, equater?: Equater) { + constructor(sequence: Sequence, converter: Converter, equater?: EqualityComparison) { super(); this.#sequence = sequence; @@ -1650,9 +1660,9 @@ class DistinctBySequence extends BaseSequence { class WhereSequence extends BaseSequence { readonly #sequence: Sequence; - readonly #predicate: FilterPredicate; + readonly #predicate: TypePredicate; - constructor(sequence: Sequence, predicate: FilterPredicate) { + constructor(sequence: Sequence, predicate: TypePredicate) { super(); this.#sequence = sequence; @@ -1674,9 +1684,9 @@ class WhereSequence extends BaseSequence extends BaseSequence { readonly #sequence: Sequence; - readonly #converter: Converter>; + readonly #converter: Converter>; - constructor(sequence: Sequence, converter: Converter>) { + constructor(sequence: Sequence, converter: Converter>) { super(); this.#sequence = sequence; @@ -1758,7 +1768,7 @@ class SkipWhileSequence extends BaseSequence { } override *iterator() { - const e = asIterable(this.#sequence.iterator()); + const e = Iterator.from(this.#sequence); for (const obj of e) { if (!this.#predicate(obj)) { @@ -1792,7 +1802,7 @@ class SkipLastSequence extends BaseSequence { } override *iterator() { - const iterator = this.#sequence.iterator(); + const iterator = Iterator.from(this.#sequence); const buffer = new Array(this.#n); // n > 0 let i = 0; @@ -1808,7 +1818,7 @@ class SkipLastSequence extends BaseSequence { i = 0; - for (const obj of asIterable(iterator)) { + for (const obj of iterator) { yield buffer[i]; buffer[i] = obj; i = (i + 1) % this.#n; @@ -1837,7 +1847,7 @@ class SkipSequence extends BaseSequence { } override *iterator() { - const iterator = this.#sequence.iterator(); + const iterator = Iterator.from(this.#sequence); let i = 0; do { @@ -1848,7 +1858,7 @@ class SkipSequence extends BaseSequence { i++; } while (i < this.#n); - yield* asIterable(iterator); + yield* iterator; } } @@ -1947,45 +1957,26 @@ class TakeSequence extends BaseSequence { } class OrderSequence extends BaseOrderedSequence { - constructor(sequence: Sequence, descending: boolean, sorter?: Comparer) { + constructor(sequence: Sequence, descending: boolean, sorter?: ComparisonOrComparer) { super(sequence, sorter, descending); } } class OrderBySequence extends BaseOrderedSequence { - readonly #selector: Converter; - - constructor(sequence: Sequence, descending: boolean, selector: Converter, sorter?: Comparer) { - super(sequence, OrderBySequence.#createSorter(selector, sorter), descending); - - this.#selector = selector; - } - - static #createSorter(selector: Converter, sorter?: Comparer) { - const _sorter = sorter ?? defaultArrayComparer; - return (a: T, b: T) => _sorter(selector(a), selector(b)); + constructor(sequence: Sequence, descending: boolean, selector: Converter, sorter?: ComparisonOrComparer) { + super(sequence, createComparerUsing(selector, sorter), descending); } } class ThenOrderSequence extends BaseOrderedSequence { - constructor(sequence: OrderedSequence, descending: boolean, sorter?: Comparer) { - super(sequence, combineComparers(sequence.comparer ?? defaultArrayComparer, sorter ?? defaultArrayComparer), descending); + constructor(sequence: OrderedSequence, descending: boolean, sorter?: ComparisonOrComparer) { + super(sequence, combineNullableComparers([sequence.comparer, sorter]), descending); } } class ThenOrderBySequence extends BaseOrderedSequence { - readonly #selector: Converter; - - constructor(sequence: OrderedSequence, descending: boolean, selector: Converter, sorter?: Comparer) { - super(sequence, ThenOrderBySequence.#createCombinedSorter(sequence.comparer, selector, sorter), descending); - - this.#selector = selector; - } - - static #createCombinedSorter(baseSorter: Comparer | undefined, selector: Converter, sorter?: Comparer) { - const _baseSorter = baseSorter ?? defaultArrayComparer; - const _sorter = sorter ?? defaultArrayComparer; - return combineComparers(_baseSorter, (a: T, b: T) => _sorter(selector(a), selector(b))); + constructor(sequence: OrderedSequence, descending: boolean, selector: Converter, sorter?: ComparisonOrComparer) { + super(sequence, combineNullableComparers([sequence.comparer, createComparerUsing(selector, sorter)]), descending); } } @@ -2110,9 +2101,9 @@ export class ZippedSequence extends BaseSequence<[T, U]> { class UnionSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, equater?: Equater) { + constructor(first: Sequence, second: Sequence, equater?: EqualityComparison) { super(); this.#first = first; @@ -2144,9 +2135,9 @@ class UnionBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { + constructor(first: Sequence, second: Sequence, selector: Converter, equater?: EqualityComparison) { super(); this.#first = first; @@ -2178,9 +2169,9 @@ class UnionBySequence extends BaseSequence { class ExceptSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, equater?: Equater) { + constructor(first: Sequence, second: Sequence, equater?: EqualityComparison) { super(); this.#first = first; @@ -2211,9 +2202,9 @@ class ExceptBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { + constructor(first: Sequence, second: Sequence, selector: Converter, equater?: EqualityComparison) { super(); this.#first = first; @@ -2244,9 +2235,9 @@ class ExceptBySequence extends BaseSequence { class IntersectSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, equater?: Equater) { + constructor(first: Sequence, second: Sequence, equater?: EqualityComparison) { super(); this.#first = first; @@ -2261,9 +2252,7 @@ class IntersectSequence extends BaseSequence { override *iterator() { const set = createEqualitySet(this.#equater); - for (const obj of this.#second) { - set.add(obj); - } + set.addAll(this.#second); for (const obj of this.#first) { if (set.remove(obj)) { @@ -2277,9 +2266,9 @@ class IntersectBySequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #selector: Converter; - readonly #equater: Equater | undefined; + readonly #equater: EqualityComparison | undefined; - constructor(first: Sequence, second: Sequence, selector: Converter, equater?: Equater) { + constructor(first: Sequence, second: Sequence, selector: Converter, equater?: EqualityComparison) { super(); this.#first = first; @@ -2341,9 +2330,9 @@ class GroupBySequence extends BaseSequence; readonly #keySelector: Converter; readonly #elementSelector: Converter; - readonly #keyEquater: Equater | undefined; + readonly #keyEquater: EqualityComparison | undefined; - constructor(sequence: Sequence, keySelector: Converter, elementSelector?: Converter, keyEquater?: Equater) { + constructor(sequence: Sequence, keySelector: Converter, elementSelector?: Converter, keyEquater?: EqualityComparison) { super(); this.#sequence = sequence; @@ -2404,7 +2393,7 @@ class ChunkedSequence extends BaseSequence> { for (const obj of this.#sequence) { chunk.push(obj); - if (chunk.length === this.#size) { + if (chunk.length >= this.#size) { yield array(chunk); chunk = []; } @@ -2416,15 +2405,53 @@ class ChunkedSequence extends BaseSequence> { } } +class ChunkedArraySequence extends BaseSequence { + readonly #sequence: Sequence; + readonly #size: number; + + constructor(sequence: Sequence, size: number) { + super(); + + this.#sequence = sequence; + this.#size = size; + } + + override nonEnumeratedCount() { + const n = this.#sequence.nonEnumeratedCount(); + return n < 0 ? super.nonEnumeratedCount() : Math.ceil(n / this.#size); + } + + override maxCount() { + return Math.ceil(this.#sequence.maxCount() / this.#size); + } + + override *iterator() { + let chunk: T[] = []; + + for (const obj of this.#sequence) { + chunk.push(obj); + + if (chunk.length >= this.#size) { + yield chunk; + chunk = []; + } + } + + if (chunk.length > 0) { + yield chunk; + } + } +} + class JoinSequence extends BaseSequence { readonly #first: Sequence; readonly #second: Sequence; readonly #firstKeySelector: Converter; readonly #secondKeySelector: Converter; readonly #resultSelector: BiConverter; - readonly #keyEquater: Equater; + readonly #keyEquater: EqualityComparison; - constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyEquater?: Equater) { + constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, keyEquater?: EqualityComparison) { super(); this.#first = first; @@ -2464,9 +2491,9 @@ class GroupJoinSequence extends BaseSequence; readonly #secondKeySelector: Converter; readonly #resultSelector: BiConverter, TResult>; - readonly #keyEquater: Equater; + readonly #keyEquater: EqualityComparison; - constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyEquater?: Equater) { + constructor(first: Sequence, second: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: BiConverter, TResult>, keyEquater?: EqualityComparison) { super(); this.#first = first; @@ -2511,9 +2538,9 @@ class RemoveSequence extends BaseSequence { readonly #sequence: Sequence; readonly #obj: T; readonly #all: boolean; - readonly #equater: Equater; + readonly #equater: EqualityComparison; - constructor(sequence: Sequence, obj: T, all?: boolean, equater?: Equater) { + constructor(sequence: Sequence, obj: T, all?: boolean, equater?: EqualityComparison) { super(); this.#sequence = sequence; @@ -2574,9 +2601,9 @@ class CacheSequence extends BaseSequence { class PartitionSequence extends BaseSequence> { readonly #sequence: Sequence; - readonly #equater: Equater; + readonly #equater: EqualityComparison; - constructor(sequence: Sequence, equater: Equater | undefined) { + constructor(sequence: Sequence, equater: EqualityComparison | undefined) { super(); this.#sequence = sequence; @@ -2605,9 +2632,9 @@ class PartitionSequence extends BaseSequence> { class PartitionBySequence extends BaseSequence> { readonly #sequence: Sequence; readonly #selector: Converter; - readonly #equater: Equater; + readonly #equater: EqualityComparison; - constructor(sequence: Sequence, selector: Converter, equater: Equater | undefined) { + constructor(sequence: Sequence, selector: Converter, equater: EqualityComparison | undefined) { super(); this.#sequence = sequence; diff --git a/src/sync/index.ts b/src/sync/index.ts index 1d4894f..07c6d58 100644 --- a/src/sync/index.ts +++ b/src/sync/index.ts @@ -74,6 +74,10 @@ export function keys(o: object): Sequence { return array(Object.keys(o)); } +export function values(o: object): Sequence { + return array(Object.values(o)); +} + export function func(f: () => T): Sequence { return new FunctionSequence(f); } @@ -138,8 +142,8 @@ export function randomSequence(random?: RandomGenerator): Sequence { return new FunctionSequence(random ?? mathRandom); } -export function concat(...sequences: Sequence[]): Sequence { - return new ConcatSequence(sequences); +export function concat(...sequences: Iterable[]): Sequence { + return new ConcatSequence(sequences.map(wrap)); } export function zip(first: Sequence, second: Sequence): Sequence<[T, T]> { diff --git a/src/sync/types.ts b/src/sync/types.ts index b3927ce..bf791a3 100644 --- a/src/sync/types.ts +++ b/src/sync/types.ts @@ -1,7 +1,9 @@ import { AsyncSequence } from "../async/types.js"; import { Collector } from "../collector/types.js"; +import { ComparisonOrComparer, Comparer } from "../comparer/types.js"; +import { EqualityComparison } from "../equality-comparer/types.js"; import { RandomOptions } from "../random/types.js"; -import { AnyPredicate, Converter, FilterPredicate, Equater, BiConverter, Accumulator, Comparer, Action } from "../types.js"; +import { AnyPredicate, Converter, TypePredicate, BiConverter, Accumulator, Action } from "../types.js"; export type SequencePipeline = (sequence: Sequence) => TResult; @@ -18,31 +20,31 @@ export interface Sequence extends Iterable { maxCount(): number; select(selector: Converter): Sequence; - selectMany(selector: Converter>): Sequence; + selectMany(selector: Converter>): Sequence; - where(predicate: FilterPredicate): Sequence; + where(predicate: TypePredicate): Sequence; where(predicate: AnyPredicate): Sequence; - groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: Equater): Sequence>; - groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: Equater): Sequence>; + groupBy(keySelector: Converter, elementSelector?: undefined, keyComparer?: EqualityComparison): Sequence>; + groupBy(keySelector: Converter, elementSelector: Converter, keyComparer?: EqualityComparison): Sequence>; - join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Sequence<[TElement, TOther]>; - join(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: Equater): Sequence; + join(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: EqualityComparison): Sequence<[TElement, TOther]>; + join(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, keyComparer?: EqualityComparison): Sequence; - groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: Equater): Sequence>; - groupJoin(sequence: Sequence, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: Equater): Sequence; + groupJoin(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector?: undefined, keyComparer?: EqualityComparison): Sequence>; + groupJoin(sequence: Iterable, firstKeySelector: Converter, secondKeySelector: Converter, resultSelector: BiConverter, TResult>, keyComparer?: EqualityComparison): Sequence; - contains(obj: TElement, equater?: Equater): boolean; + contains(obj: TElement, equater?: EqualityComparison): boolean; - sequenceEquals(sequence: Sequence, equater?: Equater): boolean; + sequenceEquals(sequence: Iterable, equater?: EqualityComparison): boolean; append(obj: TElement): Sequence; prepend(obj: TElement): Sequence; - remove(obj: TElement, all?: boolean, equater?: Equater): Sequence; + remove(obj: TElement, all?: boolean, equater?: EqualityComparison): Sequence; - concat(...sequences: Sequence[]): Sequence; + concat(...sequences: Iterable[]): Sequence; first(predicate?: AnyPredicate): TElement; firstOrDefault(predicate?: AnyPredicate, def?: TElement): TElement | undefined; @@ -60,35 +62,35 @@ export interface Sequence extends Iterable { aggregate(accumulator: Accumulator, seed?: TAccumulator): TAccumulator; aggregate(accumulator: Accumulator, seed?: TAccumulator, resultSelector?: Converter): TResult; - min(comparer?: Comparer): TElement; - minBy(selector: Converter, comparer?: Comparer): TElement; + min(comparer?: ComparisonOrComparer): TElement; + minBy(selector: Converter, comparer?: ComparisonOrComparer): TElement; - max(comparer?: Comparer): TElement; - maxBy(selector: Converter, comparer?: Comparer): TElement; + max(comparer?: ComparisonOrComparer): TElement; + maxBy(selector: Converter, comparer?: ComparisonOrComparer): TElement; - bounds(comparer?: Comparer): { min: TElement, max: TElement; }; - boundsBy(selector: Converter, comparer?: Comparer): { min: TElement, max: TElement; }; + bounds(comparer?: ComparisonOrComparer): [min: TElement, max: TElement]; + boundsBy(selector: Converter, comparer?: ComparisonOrComparer): [min: TElement, max: TElement]; - order(comparer?: Comparer): OrderedSequence; - orderBy(selector: Converter, comparer?: Comparer): OrderedSequence; + order(comparer?: ComparisonOrComparer): OrderedSequence; + orderBy(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence; - orderDescending(comparer?: Comparer): OrderedSequence; - orderByDescending(selector: Converter, comparer?: Comparer): OrderedSequence; + orderDescending(comparer?: ComparisonOrComparer): OrderedSequence; + orderByDescending(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence; - partition(equater?: Equater): Sequence>; - partitionBy(selector: Converter, equater?: Equater): Sequence>; + partition(equater?: EqualityComparison): Sequence>; + partitionBy(selector: Converter, equater?: EqualityComparison): Sequence>; - distinct(equater?: Equater): Sequence; - distinctBy(selector: Converter, equater?: Equater): Sequence; + distinct(equater?: EqualityComparison): Sequence; + distinctBy(selector: Converter, equater?: EqualityComparison): Sequence; - union(sequence: Sequence, equater?: Equater): Sequence; - unionBy(sequence: Sequence, selector: Converter, equater?: Equater): Sequence; + union(sequence: Iterable, equater?: EqualityComparison): Sequence; + unionBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison): Sequence; - except(sequence: Sequence, equater?: Equater): Sequence; - exceptBy(sequence: Sequence, selector: Converter, equater?: Equater): Sequence; + except(sequence: Iterable, equater?: EqualityComparison): Sequence; + exceptBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison): Sequence; - intersect(sequence: Sequence, equater?: Equater): Sequence; - intersectBy(sequence: Sequence, selector: Converter, equater?: Equater): Sequence; + intersect(sequence: Iterable, equater?: EqualityComparison): Sequence; + intersectBy(sequence: Iterable, selector: Converter, equater?: EqualityComparison): Sequence; all(predicate: AnyPredicate): boolean; any(predicate: AnyPredicate): boolean; @@ -108,13 +110,14 @@ export interface Sequence extends Iterable { forEach(action: Action): void; - zip(sequence: Sequence): Sequence<[TElement, TOther]>; + zip(sequence: Iterable): Sequence<[TElement, TOther]>; indexed(): Sequence<[number, TElement]>; reversed(): Sequence; chunked(size: number): Sequence>; + chunked(size: number, asArray: true): Sequence; chunked(size: number, transformer: SequencePipeline): Sequence; random(options?: RandomOptions): TElement | undefined; @@ -139,13 +142,13 @@ export interface GroupedSequence extends Sequence { } export interface OrderedSequence extends Sequence { - get comparer(): Comparer | undefined; + get comparer(): Comparer; - thenSelf(comparer?: Comparer): OrderedSequence; + thenSelf(comparer?: ComparisonOrComparer): OrderedSequence; - thenBy(selector: Converter, comparer?: Comparer): OrderedSequence; + thenBy(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence; - thenSelfDescending(comparer?: Comparer): OrderedSequence; + thenSelfDescending(comparer?: ComparisonOrComparer): OrderedSequence; - thenByDescending(selector: Converter, comparer?: Comparer): OrderedSequence; + thenByDescending(selector: Converter, comparer?: ComparisonOrComparer): OrderedSequence; } diff --git a/src/types.ts b/src/types.ts index 79c3b20..1dae552 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,15 +1,10 @@ -import { AsyncSequence } from "./async/types.js"; -import { Sequence } from "./sync/types.js"; - export type Predicate = (obj: T) => boolean; export type AnyPredicate = (obj: T) => unknown; -export type FilterPredicate = (obj: TElement) => obj is TFiltered; +export type TypePredicate = (obj: TElement) => obj is TType; export type Converter = (obj: TFrom) => TTo; export type BiConverter = (first: TFromFirst, second: TFromSecond) => TTo; export type Action = (obj: T) => unknown; export type Accumulator = (acc: TAccumulator, obj: TElement) => TAccumulator; -export type Comparer = (first: T, second: T) => number; -export type Equater = (first: T, second: T) => boolean; export type MaybeAsyncIterable = Iterable | AsyncIterable; export type MaybeAsyncIterator = Iterator | AsyncIterator; @@ -18,12 +13,13 @@ export type AsyncFunction any> = TFunc extends ( export type MaybePromise = T | Promise; export type MaybePromiseLike = T | PromiseLike; export type MaybeAsyncFunction any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => MaybePromise : never; -export type MaybeAsyncSequence = Sequence | AsyncSequence; export type AsyncPredicate = AsyncFunction>; export type AsyncAnyPredicate = AsyncFunction>; +export type AsyncTypePredicate = AsyncFunction>; export type MaybeAsyncPredicate = MaybeAsyncFunction>; export type MaybeAsyncAnyPredicate = MaybeAsyncFunction>; +export type MaybeAsyncTypePredicate = MaybeAsyncFunction>; export type AsyncConverter = AsyncFunction>; export type MaybeAsyncConverter = MaybeAsyncFunction>; export type AsyncBiConverter = AsyncFunction>; @@ -32,7 +28,4 @@ export type AsyncAction = AsyncFunction>; export type MaybeAsyncAction = MaybeAsyncFunction>; export type AsyncAccumulator = AsyncFunction>; export type MaybeAsyncAccumulator = MaybeAsyncFunction>; -export type AsyncComparer = AsyncFunction>; -export type MaybeAsyncComparer = MaybeAsyncFunction>; -export type AsyncEquater = AsyncFunction>; -export type MaybeAsyncEquater = MaybeAsyncFunction>; + diff --git a/src/utils.ts b/src/utils.ts index e5b239d..f394984 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,95 +1,17 @@ -import { Comparer, MaybeAsyncComparer, AsyncComparer } from "./types.js"; +export type Nullable = T | null | undefined; export function isDefined(obj: T): obj is NonNullable { return obj !== undefined && obj !== null; } -export function isIterable(obj: any): obj is Iterable { - return isDefined(obj) && typeof obj[Symbol.iterator] === "function"; -} - export function isAsyncIterable(obj: any): obj is AsyncIterable { return isDefined(obj) && typeof obj[Symbol.asyncIterator] === "function"; } -export function identity(obj: T) { - return obj; -} - -export function looseEquals(a: T, b: T) { - return a == b; -} - -export function strictEquals(a: T, b: T) { - return a === b; -} - -export function referenceEquals(a: T, b: T) { - return Object.is(a, b); -} - -export function numberCompare(a: T, b: T) { - return a - b; -} - -export function operatorCompare(a: any, b: any) { - return a < b ? -1 : a > b ? 1 : 0; -} - -export function defaultArrayComparer(a: T, b: T) { - if (a === undefined) { - if (b === undefined) { - return 0; - } - - return 1; - } - - if (b === undefined) { - return -1; - } - - const aStr = `${a}`, bStr = `${b}`; - - return aStr > bStr ? 1 : aStr < bStr ? -1 : 0; -} - -export function combineComparers(first: Comparer, second: Comparer): Comparer { - return (a, b) => first(a, b) || second(a, b); -} - -export function combineAsyncComparers(first: MaybeAsyncComparer, second: MaybeAsyncComparer): AsyncComparer { - return async (a, b) => await first(a, b) || await second(a, b); -} - -export function reverseComparer(comparer: Comparer): Comparer { - return (a, b) => comparer(b, a); -} - -export function reverseAsyncComparer(comparer: MaybeAsyncComparer): AsyncComparer { - return async (a, b) => await comparer(b, a); -} - export function asArray(iterable: Iterable) { return Array.isArray(iterable) ? iterable : Array.from(iterable); } -class WrappedIterator implements Iterable { - readonly #iterator: Iterator; - - constructor(iterator: Iterator) { - this.#iterator = iterator; - } - - [Symbol.iterator]() { - return this.#iterator; - } -} - -export function asIterable(iterator: Iterator): Iterable { - return isIterable(iterator) ? iterator : new WrappedIterator(iterator); -} - class WrappedAsyncIterator implements AsyncIterable { readonly #iterator: AsyncIterator; @@ -115,14 +37,14 @@ const _emptyIterableIterator = new class EmptyIterableIterator implements Iterab return { done: true, value: undefined }; } - return(value?: any): IteratorResult { + return(_value?: any): IteratorResult { throw new Error("Method not implemented."); } - throw(e?: any): IteratorResult { + throw(_e?: any): IteratorResult { throw new Error("Method not implemented."); } -} +}; export function emptyIterableIterator(): IterableIterator { return _emptyIterableIterator; diff --git a/tsconfig.json b/tsconfig.json index 4dd3748..2b511f3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "module": "Node16", - "target": "ES2023", - "moduleResolution": "Node16", + "module": "NodeNext", + "target": "ESNext", + "moduleResolution": "nodenext", "rootDir": "src", "outDir": "build", "outFile": "build/index.ts", @@ -13,7 +13,8 @@ "allowUnreachableCode": true, "declaration": true, "declarationMap": true, - "emitDeclarationOnly": true + "emitDeclarationOnly": true, + "composite": true }, "include": [ "src"