1
0

Compare commits

...

50 Commits

Author SHA1 Message Date
d0164640a1 remove declaration 2026-04-08 22:52:14 +02:00
ae5305f3c6 sync 2026-04-06 18:45:31 +02:00
31c0757780 update deps 2026-04-06 18:39:58 +02:00
696729d2c2 remove Sequences.ofPropertyKeys 2025-11-22 14:09:38 +01:00
bdea643256 properly implement comparer and equality comparer combination 2025-11-22 14:09:37 +01:00
4e5dfd7134 add cartesian product 2025-11-22 14:09:35 +01:00
955acc6c96 add async equality comparer and broaden to comparison or comparer 2025-11-22 14:09:33 +01:00
c7a6d6880c update packages and tsconfig 2025-11-22 14:09:32 +01:00
400644e079 refactor 2025-11-22 14:09:28 +01:00
f6a9dd97ed use consistent wording 2025-11-22 14:09:26 +01:00
806d735c35 sync 2025-11-22 14:09:22 +01:00
637991c394 move identity function to utils 2025-11-22 14:09:19 +01:00
afa4c8a684 improve typing of element finding utils 2025-11-22 14:09:18 +01:00
75219bc716 move BitArray in its namerspace 2025-11-22 14:09:18 +01:00
c98e462142 sync 2025-11-22 14:09:18 +01:00
9af08e71fc sync 2025-11-22 14:09:10 +01:00
c2196e11c9 add build dir to gitignore 2024-08-15 00:46:24 +02:00
0b03475f66 sync local repositories
add booleanish predicates
enable custom inspection
default to sequence of any when sequence type is unknown
2024-08-15 00:40:45 +02:00
aedad87c8e switch to tsx and update build config 2024-08-15 00:33:11 +02:00
e63684175b add esbuild 2024-06-16 14:45:15 +02:00
e915e88e0a remove asArray 2024-05-31 12:17:54 +02:00
6c7eac8217 some BitArray refactor 2024-05-23 13:51:38 +02:00
654151e06e refactor 2024-05-18 17:22:35 +02:00
8c6693ad98 make chunked return sequence of sequences 2024-05-18 13:39:26 +02:00
719cb660e6 correctly initialize and expose empty sequences 2024-05-17 22:46:01 +02:00
e68b80c8eb implement awaited sequence 2024-05-17 22:45:28 +02:00
595d9fd5fc sequence type fixes and refactor 2024-05-17 22:12:16 +02:00
85033018cf do not export all sequence impls 2024-05-13 23:17:01 +02:00
46a3b1741e add sequence partition operation 2024-05-13 23:12:46 +02:00
c38092c60e add size to async equality map 2024-05-13 22:43:39 +02:00
7553b70882 add element iterators to async collections 2024-05-13 22:42:43 +02:00
1f8e8039b6 make async equality collections sync iterables 2024-05-13 22:39:59 +02:00
eda8c8cf5d add equality collection element iterators 2024-05-13 22:24:57 +02:00
35d284f030 add equality map size 2024-05-13 22:24:09 +02:00
05390027f8 rename lib to sequence-js 2024-05-11 16:57:34 +02:00
486abefba6 change project structure to remove namespaces 2024-05-11 16:46:47 +02:00
727cdc3b2b fix AsyncEnumerable types 2024-05-11 12:56:12 +02:00
679a61f040 move types around 2024-05-09 23:55:40 +02:00
fccebc61b6 refactor modules into namespaces 2024-05-09 18:11:50 +02:00
ab11074744 allow building JS files 2024-05-09 17:37:23 +02:00
6c0713d591 harmonize sync and async implementations 2024-05-09 17:35:55 +02:00
a1f43287a0 allow creating RandomPicker from iterable and separately specified length 2024-05-09 16:28:05 +02:00
b48f97d1c3 add empty BitArray implementation 2024-05-09 16:26:50 +02:00
3af744c24d refactor random options init and merge 2024-05-08 20:42:49 +02:00
a140245c05 refactor BitArray to be interface with hidden implementations 2024-05-08 20:41:41 +02:00
36c06874fa add collect to async enumerable 2024-05-08 20:40:51 +02:00
dd1f271674 rename isEquals to referenceEquals 2024-05-08 20:40:22 +02:00
a2e525c2da remove parameter types 2024-05-08 20:40:09 +02:00
6cb81d452d don't wrap IterableIterator 2024-05-08 20:39:12 +02:00
675c8a5b12 use square brackets for array creation 2024-05-08 20:38:14 +02:00
44 changed files with 9188 additions and 5995 deletions

4
.gitignore vendored
View File

@@ -1 +1,3 @@
/node_modules
/node_modules
/build
/src/test.ts

676
package-lock.json generated
View File

@@ -1,185 +1,577 @@
{
"name": "enumerable",
"name": "sequence-js",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "enumerable",
"name": "sequence-js",
"version": "1.0.0",
"license": "ISC",
"devDependencies": {
"@types/node": "^20.12.7",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
"@types/node": "^25.5.0",
"tsx": "^4.19.4",
"typescript": "^6.0.2"
}
},
"node_modules/@cspotcode/source-map-support": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
"integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz",
"integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==",
"cpu": [
"ppc64"
],
"dev": true,
"dependencies": {
"@jridgewell/trace-mapping": "0.3.9"
},
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=12"
"node": ">=18"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"node_modules/@esbuild/android-arm": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz",
"integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=6.0.0"
"node": ">=18"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.4.15",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
"integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
"dev": true
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
"integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
"node_modules/@esbuild/android-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz",
"integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==",
"cpu": [
"arm64"
],
"dev": true,
"dependencies": {
"@jridgewell/resolve-uri": "^3.0.3",
"@jridgewell/sourcemap-codec": "^1.4.10"
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
"integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
"dev": true
"node_modules/@esbuild/android-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz",
"integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@tsconfig/node12": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
"integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
"dev": true
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz",
"integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@tsconfig/node14": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
"integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
"dev": true
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz",
"integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@tsconfig/node16": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
"dev": true
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz",
"integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz",
"integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz",
"integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz",
"integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz",
"integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz",
"integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz",
"integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz",
"integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz",
"integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz",
"integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz",
"integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz",
"integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz",
"integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz",
"integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz",
"integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz",
"integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz",
"integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz",
"integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz",
"integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz",
"integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@types/node": {
"version": "20.12.8",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
"integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
"version": "25.5.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.2.tgz",
"integrity": "sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~5.26.4"
"undici-types": "~7.18.0"
}
},
"node_modules/acorn": {
"version": "8.11.3",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
"integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
"node_modules/esbuild": {
"version": "0.27.7",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz",
"integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=0.4.0"
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.7",
"@esbuild/android-arm": "0.27.7",
"@esbuild/android-arm64": "0.27.7",
"@esbuild/android-x64": "0.27.7",
"@esbuild/darwin-arm64": "0.27.7",
"@esbuild/darwin-x64": "0.27.7",
"@esbuild/freebsd-arm64": "0.27.7",
"@esbuild/freebsd-x64": "0.27.7",
"@esbuild/linux-arm": "0.27.7",
"@esbuild/linux-arm64": "0.27.7",
"@esbuild/linux-ia32": "0.27.7",
"@esbuild/linux-loong64": "0.27.7",
"@esbuild/linux-mips64el": "0.27.7",
"@esbuild/linux-ppc64": "0.27.7",
"@esbuild/linux-riscv64": "0.27.7",
"@esbuild/linux-s390x": "0.27.7",
"@esbuild/linux-x64": "0.27.7",
"@esbuild/netbsd-arm64": "0.27.7",
"@esbuild/netbsd-x64": "0.27.7",
"@esbuild/openbsd-arm64": "0.27.7",
"@esbuild/openbsd-x64": "0.27.7",
"@esbuild/openharmony-arm64": "0.27.7",
"@esbuild/sunos-x64": "0.27.7",
"@esbuild/win32-arm64": "0.27.7",
"@esbuild/win32-ia32": "0.27.7",
"@esbuild/win32-x64": "0.27.7"
}
},
"node_modules/acorn-walk": {
"version": "8.3.2",
"resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
"integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=0.4.0"
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/arg": {
"version": "4.1.3",
"resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
"integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
"dev": true
},
"node_modules/create-require": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
"dev": true
},
"node_modules/diff": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
"integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
"dev": true,
"engines": {
"node": ">=0.3.1"
}
},
"node_modules/make-error": {
"version": "1.3.6",
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true
},
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
"integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
"node_modules/get-tsconfig": {
"version": "4.13.7",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.7.tgz",
"integrity": "sha512-7tN6rFgBlMgpBML5j8typ92BKFi2sFQvIdpAqLA2beia5avZDrMs0FLZiM5etShWq5irVyGcGMEA1jcDaK7A/Q==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "^0.8.0",
"@tsconfig/node10": "^1.0.7",
"@tsconfig/node12": "^1.0.7",
"@tsconfig/node14": "^1.0.0",
"@tsconfig/node16": "^1.0.2",
"acorn": "^8.4.1",
"acorn-walk": "^8.1.1",
"arg": "^4.1.0",
"create-require": "^1.1.0",
"diff": "^4.0.1",
"make-error": "^1.1.1",
"v8-compile-cache-lib": "^3.0.1",
"yn": "3.1.1"
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/tsx": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"ts-node": "dist/bin.js",
"ts-node-cwd": "dist/bin-cwd.js",
"ts-node-esm": "dist/bin-esm.js",
"ts-node-script": "dist/bin-script.js",
"ts-node-transpile-only": "dist/bin-transpile.js",
"ts-script": "dist/bin-script-deprecated.js"
"tsx": "dist/cli.mjs"
},
"peerDependencies": {
"@swc/core": ">=1.2.50",
"@swc/wasm": ">=1.2.50",
"@types/node": "*",
"typescript": ">=2.7"
"engines": {
"node": ">=18.0.0"
},
"peerDependenciesMeta": {
"@swc/core": {
"optional": true
},
"@swc/wasm": {
"optional": true
}
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/typescript": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz",
"integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==",
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz",
"integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -189,25 +581,11 @@
}
},
"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==",
"dev": true
},
"node_modules/v8-compile-cache-lib": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
"integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
"dev": true
},
"node_modules/yn": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
"version": "7.18.2",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
"integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
"dev": true,
"engines": {
"node": ">=6"
}
"license": "MIT"
}
}
}

View File

@@ -1,18 +1,28 @@
{
"name": "enumerable",
"name": "sequence-js",
"version": "1.0.0",
"description": "Implementation of Enumerable API from .NET",
"description": "Synchronous and asynchronous sequence manipulation library inspired by .NET Enumerables and Java Streams",
"main": "src/index.ts",
"type": "module",
"scripts": {
"start": "node --import ./ts-loader.js src/index.ts",
"test": "echo \"Error: no test specified\" && exit 1"
"start": "tsx src/index.ts",
"test": "tsx src/test.ts"
},
"author": "Hervé BECHER",
"license": "ISC",
"devDependencies": {
"@types/node": "^20.12.7",
"ts-node": "^10.9.2",
"typescript": "^5.4.5"
"@types/node": "^25.5.0",
"tsx": "^4.19.4",
"typescript": "^6.0.2"
},
"exports": {
".": {
"import": "./src/index.ts",
"types": "./src/index.ts"
},
"./*": {
"import": "./src/*.ts",
"types": "./src/*.ts"
}
}
}

File diff suppressed because it is too large Load Diff

2366
src/async/impl.ts Normal file

File diff suppressed because it is too large Load Diff

109
src/async/index.ts Normal file
View File

@@ -0,0 +1,109 @@
import { wrap as wrapSync } from "../sync/index.js";
import { Sequence } from "../sync/types.js";
import { MaybeAsyncGenerator, MaybeAsyncIterable, MaybePromiseLike } from "../types.js";
import { isAsyncIterable } from "../utils.js";
import { WrappedSequence, WrappedAsyncIterable, WrappedObjectAsync, WrappedArrayAsync, WrappedArrayLikeAsync, FunctionAsyncSequence, GeneratorAsyncSequence, RangeAsyncSequence, RepeatForeverAsyncSequence, RepeatAsyncSequence, AsyncSequenceMarker, EMPTY, ConcatAsyncSequence, WrappedPromise } from "./impl.js";
import { AsyncSequence } from "./types.js";
export function asAsync<T>(sequence: Sequence<MaybePromiseLike<T>>): AsyncSequence<T> {
return new WrappedSequence(sequence);
}
export function wrapPromise<T>(promise: Promise<MaybeAsyncIterable<T>>): AsyncSequence<T> {
return new WrappedPromise(promise);
}
export function wrap<T>(iterable: MaybeAsyncIterable<MaybePromiseLike<T>>): AsyncSequence<T> {
if (isAsyncSequence<T>(iterable)) {
return iterable;
}
if (isAsyncIterable(iterable)) {
return sequence(iterable);
}
return asAsync(wrapSync(iterable));
}
export function sequence<T>(iterable: AsyncIterable<MaybePromiseLike<T>>): AsyncSequence<T> {
return new WrappedAsyncIterable(iterable);
}
export function empty<T>(): AsyncSequence<T> {
return EMPTY;
}
export function single<T>(obj: MaybePromiseLike<T>): AsyncSequence<T> {
return new WrappedObjectAsync(obj);
}
export function array<T>(array: ReadonlyArray<MaybePromiseLike<T>>): AsyncSequence<T> {
return new WrappedArrayAsync(array);
}
export function arrayLike<T>(arrayLike: ArrayLike<MaybePromiseLike<T>>): AsyncSequence<T> {
return new WrappedArrayLikeAsync(arrayLike);
}
export function of<T>(...elements: MaybePromiseLike<T>[]): AsyncSequence<T> {
switch (elements.length) {
case 0:
return empty();
case 1:
return single(elements[0]);
default:
return array(elements);
}
}
export function concat<T>(...sequences: AsyncSequence<T>[]): AsyncSequence<T> {
return new ConcatAsyncSequence<T>(sequences);
}
export function func<T>(f: () => MaybePromiseLike<T>): AsyncSequence<T> {
return new FunctionAsyncSequence(f);
}
export function generator<T>(generator: () => MaybeAsyncGenerator<MaybePromiseLike<T>>): AsyncSequence<T> {
return new GeneratorAsyncSequence(generator);
}
export function range(max: number): AsyncSequence<number>
export function range(min: number, max: number): AsyncSequence<number>
export function range(min: number, max: number, step: number): AsyncSequence<number>
export function range(a: number, b?: number, c?: number): AsyncSequence<number> {
if (b === undefined) {
b = a;
a = 0;
}
if (c === undefined) {
c = 1;
}
return new RangeAsyncSequence(a, b, c);
}
export function repeat<T>(value: MaybePromiseLike<T>, count?: number): AsyncSequence<T> {
if (count == undefined) {
return new RepeatForeverAsyncSequence(value);
}
if (count < 0) {
throw new Error("count < 0");
}
if (count === 0) {
return empty();
}
if (count === 1) {
return new WrappedObjectAsync(value);
}
return new RepeatAsyncSequence(value, count);
}
export function isAsyncSequence<T = any>(obj: any): obj is AsyncSequence<T> {
return obj instanceof AsyncSequenceMarker;
}

151
src/async/types.ts Normal file
View File

@@ -0,0 +1,151 @@
import { Collector } from "../collector/types.js";
import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "../comparer/types.js";
import { MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { AsyncRandomOptions } from "../random/types.js";
import { MaybeAsyncAnyPredicate, MaybeAsyncConverter, MaybeAsyncBiConverter, MaybeAsyncAccumulator, MaybeAsyncAction, MaybeAsyncFunction, MaybePromise, MaybeAsyncIterable } from "../types.js";
export type AsyncSequencePipeline<TElement, TResult> = MaybeAsyncFunction<(sequence: AsyncSequence<TElement>) => TResult>;
export type AsyncSequenceElement<TAsyncSequence extends AsyncSequence<any>> = TAsyncSequence extends AsyncSequence<infer TElement> ? TElement : never;
export interface AsyncSequence<TElement> extends AsyncIterable<TElement> {
iterator(): AsyncIterator<TElement>;
apply<TResult>(pipeline: AsyncSequencePipeline<TElement, TResult>): MaybePromise<TResult>;
count(predicate?: MaybeAsyncAnyPredicate<TElement>): Promise<number>;
nonEnumeratedCount(): Promise<number>;
fastCount(): Promise<number>;
maxCount(): Promise<number>;
select<TResult>(selector: MaybeAsyncConverter<TElement, TResult>): AsyncSequence<TResult>;
selectMany<TResult>(selector: MaybeAsyncConverter<TElement, MaybeAsyncIterable<TResult>>): AsyncSequence<TResult>;
where<TFiltered extends TElement>(predicate: MaybeAsyncAnyPredicate<TElement>): AsyncSequence<TFiltered>;
where(predicate: MaybeAsyncAnyPredicate<TElement>): AsyncSequence<TElement>;
groupBy<TKey>(keySelector: MaybeAsyncConverter<TElement, TKey>, elementSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<GroupedAsyncSequence<TKey, TElement>>;
groupBy<TKey, TResult>(keySelector: MaybeAsyncConverter<TElement, TKey>, elementSelector: MaybeAsyncConverter<TElement, TResult>, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<GroupedAsyncSequence<TKey, TResult>>;
join<TOther, TKey>(sequence: MaybeAsyncIterable<TOther>, firstKeySelector: MaybeAsyncConverter<TElement, TKey>, secondKeySelector: MaybeAsyncConverter<TOther, TKey>, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<[TElement, TOther]>;
join<TOther, TKey, TResult>(sequence: MaybeAsyncIterable<TOther>, firstKeySelector: MaybeAsyncConverter<TElement, TKey>, secondKeySelector: MaybeAsyncConverter<TOther, TKey>, resultSelector: MaybeAsyncBiConverter<TElement, TOther, TResult>, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<TResult>;
groupJoin<TOther, TKey>(sequence: MaybeAsyncIterable<TOther>, firstKeySelector: MaybeAsyncConverter<TElement, TKey>, secondKeySelector: MaybeAsyncConverter<TOther, TKey>, resultSelector?: undefined, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<GroupedAsyncSequence<TElement, TOther>>;
groupJoin<TOther, TKey, TResult>(sequence: MaybeAsyncIterable<TOther>, firstKeySelector: MaybeAsyncConverter<TElement, TKey>, secondKeySelector: MaybeAsyncConverter<TOther, TKey>, resultSelector: MaybeAsyncBiConverter<TElement, MaybeAsyncIterable<TOther>, TResult>, keyComparer?: MaybeAsyncEqualityComparisonOrComparer<TKey>): AsyncSequence<TResult>;
contains(obj: TElement, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): Promise<boolean>;
sequenceEquals(sequence: MaybeAsyncIterable<TElement>, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): Promise<boolean>;
append(obj: TElement): AsyncSequence<TElement>;
prepend(obj: TElement): AsyncSequence<TElement>;
remove(obj: TElement, all?: boolean, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<TElement>;
concat(...sequences: MaybeAsyncIterable<TElement>[]): AsyncSequence<TElement>;
first(predicate?: MaybeAsyncAnyPredicate<TElement>): Promise<TElement>;
firstOrDefault(predicate?: MaybeAsyncAnyPredicate<TElement>, def?: TElement): Promise<TElement | undefined>;
last(predicate?: MaybeAsyncAnyPredicate<TElement>): Promise<TElement>;
lastOrDefault(predicate?: MaybeAsyncAnyPredicate<TElement>, def?: TElement): Promise<TElement | undefined>;
single(predicate?: MaybeAsyncAnyPredicate<TElement>): Promise<TElement>;
singleOrDefault(predicate?: MaybeAsyncAnyPredicate<TElement>, def?: TElement): Promise<TElement | undefined>;
elementAt(index: number): Promise<TElement>;
elementAtOrDefault(index: number, def?: TElement): Promise<TElement | undefined>;
aggregate(accumulator: MaybeAsyncAccumulator<TElement, TElement>): Promise<TElement>;
aggregate<TAccumulator>(accumulator: MaybeAsyncAccumulator<TElement, TAccumulator>, seed?: TAccumulator): Promise<TAccumulator>;
aggregate<TAccumulator, TResult>(accumulator: MaybeAsyncAccumulator<TElement, TAccumulator>, seed?: TAccumulator, resultSelector?: MaybeAsyncConverter<TAccumulator, TResult>): Promise<TResult>;
min(): Promise<TElement>;
minBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>): Promise<TElement>;
max(): Promise<TElement>;
maxBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>): Promise<TElement>;
bounds(comparer?: MaybeAsyncComparisonOrComparer<TElement>): Promise<[min: TElement, max: TElement]>;
boundsBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, comparer?: MaybeAsyncComparisonOrComparer<TBy>): Promise<[min: TElement, max: TElement]>;
order(comparer?: MaybeAsyncComparisonOrComparer<TElement>): OrderedAsyncSequence<TElement>;
orderBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, comparer?: MaybeAsyncComparisonOrComparer<TBy>): OrderedAsyncSequence<TElement>;
orderDescending(comparer?: MaybeAsyncComparisonOrComparer<TElement>): OrderedAsyncSequence<TElement>;
orderByDescending<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, comparer?: MaybeAsyncComparisonOrComparer<TBy>): OrderedAsyncSequence<TElement>;
partition(equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<AsyncSequence<TElement>>;
partitionBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, equater?: MaybeAsyncEqualityComparisonOrComparer<TBy>): AsyncSequence<AsyncSequence<TElement>>;
distinct(equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<TElement>;
distinctBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, equater?: MaybeAsyncEqualityComparisonOrComparer<TBy>): AsyncSequence<TElement>;
union(sequence: MaybeAsyncIterable<TElement>, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<TElement>;
unionBy<TBy>(sequence: MaybeAsyncIterable<TElement>, selector: MaybeAsyncConverter<TElement, TBy>, equater?: MaybeAsyncEqualityComparisonOrComparer<TBy>): AsyncSequence<TElement>;
except(sequence: MaybeAsyncIterable<TElement>, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<TElement>;
exceptBy<TBy>(sequence: MaybeAsyncIterable<TElement>, selector: MaybeAsyncConverter<TElement, TBy>, equater?: MaybeAsyncEqualityComparisonOrComparer<TBy>): AsyncSequence<TElement>;
intersect(sequence: MaybeAsyncIterable<TElement>, equater?: MaybeAsyncEqualityComparisonOrComparer<TElement>): AsyncSequence<TElement>;
intersectBy<TBy>(sequence: MaybeAsyncIterable<TElement>, selector: MaybeAsyncConverter<TElement, TBy>, equater?: MaybeAsyncEqualityComparisonOrComparer<TBy>): AsyncSequence<TElement>;
all(predicate: MaybeAsyncAnyPredicate<TElement>): Promise<boolean>;
any(predicate: MaybeAsyncAnyPredicate<TElement>): Promise<boolean>;
any(): Promise<boolean>;
none(predicate: MaybeAsyncAnyPredicate<TElement>): Promise<boolean>;
none(): Promise<boolean>;
skip(n: number): AsyncSequence<TElement>;
skipLast(n: number): AsyncSequence<TElement>;
skipWhile(condition: MaybeAsyncAnyPredicate<TElement>): AsyncSequence<TElement>;
take(n: number): AsyncSequence<TElement>;
takeLast(n: number): AsyncSequence<TElement>;
takeWhile(condition: MaybeAsyncAnyPredicate<TElement>): AsyncSequence<TElement>;
peek(action: MaybeAsyncAction<TElement>): AsyncSequence<TElement>;
forEach(action: MaybeAsyncAction<TElement>): Promise<void>;
zip<TOther>(sequence: MaybeAsyncIterable<TOther>): AsyncSequence<[TElement, TOther]>;
cartesianProduct<TOther>(sequence: MaybeAsyncIterable<TOther>): AsyncSequence<[TElement, TOther]>;
indexed(): AsyncSequence<[number, TElement]>;
reversed(): AsyncSequence<TElement>;
chunked(size: number): AsyncSequence<AsyncSequence<TElement>>;
chunked<TResult>(size: number, transformer: AsyncSequencePipeline<TElement, TResult>): AsyncSequence<TResult>;
random(options?: AsyncRandomOptions<TElement>): Promise<TElement | undefined>;
cached(): AsyncSequence<TElement>;
asArray(): Promise<TElement[]>;
toArray(): Promise<TElement[]>;
toMap<TKey>(keySelector: MaybeAsyncConverter<TElement, TKey>): Promise<Map<TKey, TElement>>;
toMap<TKey, TValue>(keySelector: MaybeAsyncConverter<TElement, TKey>, valueSelector: MaybeAsyncConverter<TElement, TValue>): Promise<Map<TKey, TValue>>;
toSet(): Promise<Set<TElement>>;
toObject<TKey extends PropertyKey>(keySelector: MaybeAsyncConverter<TElement, TKey>): Promise<Record<TKey, TElement>>;
toObject<TKey extends PropertyKey, TValue>(keySelector: MaybeAsyncConverter<TElement, TKey>, valueSelector: MaybeAsyncConverter<TElement, TValue>): Promise<Record<TKey, TValue>>;
collect<TResult>(collector: Collector<TElement, any, TResult>): Promise<TResult>;
}
export interface GroupedAsyncSequence<TKey, TElement> extends AsyncSequence<TElement> {
get key(): TKey;
}
export interface OrderedAsyncSequence<TElement> extends AsyncSequence<TElement> {
get comparer(): AsyncComparer<TElement>;
thenSelf(comparer?: MaybeAsyncComparisonOrComparer<TElement>): OrderedAsyncSequence<TElement>;
thenBy<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, comparer?: MaybeAsyncComparisonOrComparer<TBy>): OrderedAsyncSequence<TElement>;
thenSelfDescending(comparer?: MaybeAsyncComparisonOrComparer<TElement>): OrderedAsyncSequence<TElement>;
thenByDescending<TBy>(selector: MaybeAsyncConverter<TElement, TBy>, comparer?: MaybeAsyncComparisonOrComparer<TBy>): OrderedAsyncSequence<TElement>;
}

View File

@@ -1,230 +0,0 @@
import { join } from "./collector.js";
import { arrayLike, sequence } from "./sync.js";
import { asArray, isDefined } from "./utils.js";
const BYTE_SIZE = Uint8Array.BYTES_PER_ELEMENT * 8;
const FULL_BYTE = getMask(BYTE_SIZE);
function getMask(bitCount: number) {
return ~(~0b0 << bitCount);
}
function getByteIndex(index: number) {
return Math.floor(index / BYTE_SIZE);
}
function getBitIndex(index: number) {
return index % BYTE_SIZE;
}
function setBit(byte: number, bitIndex: number, value: boolean) {
const mask = 0b1 << bitIndex;
return value ? (byte | mask) : (byte & ~mask);
}
function* yieldBits(byte: number, bitCount: number) {
for (let i = 0; i < bitCount; i++) {
yield Boolean((byte >>> i) & 0b1);
}
}
function* getBytes(bits: Iterable<boolean>) {
const bitIterator = bits[Symbol.iterator]();
while (true) {
let next = bitIterator.next();
if (next.done) {
break;
}
let byte = 0;
for (let i = 0; i < BYTE_SIZE; i++) {
byte |= Number(next.value) << i;
next = bitIterator.next();
if (next.done) {
break;
}
}
yield byte;
}
}
export class BitArray implements Iterable<boolean> {
readonly #length: number;
readonly #bits: Uint8Array;
readonly #wholeBytes: number;
readonly #remainingBits: number;
readonly #remainingBitsMask: number;
public constructor(length: number) {
const exactBytes = length / BYTE_SIZE;
const remainingBits = length % BYTE_SIZE;
this.#length = length;
this.#bits = new Uint8Array(Math.ceil(exactBytes));
this.#wholeBytes = Math.floor(exactBytes);
this.#remainingBits = remainingBits;
this.#remainingBitsMask = getMask(remainingBits);
}
public static from(bits: Iterable<boolean>) {
const arr = asArray(bits);
const result = new this(arr.length);
let i = 0;
for (const bit of arr) {
result.set(i++, bit);
}
return result;
}
public static of(...bits: boolean[]) {
return this.from(bits);
}
*[Symbol.iterator]() {
for (let i = 0; i < this.#wholeBytes; i++) {
yield* yieldBits(this.#bits[i], BYTE_SIZE);
}
if (this.#remainingBits > 0) {
yield* yieldBits(this.#bits[this.#wholeBytes], this.#remainingBits);
}
}
#ensureValidIndex(index: number) {
if (index < 0 || index >= this.#length) {
throw new RangeError("The index is outside the BitArray range.");
}
}
#ensureSameSize(other: BitArray) {
if (this.#length !== other.length) {
throw new TypeError("The BitArrays do not have the same length.");
}
}
public get length() {
return this.#length;
}
public get(index: number) {
this.#ensureValidIndex(index);
return Boolean((this.#bits[getByteIndex(index)] >>> getBitIndex(index)) & 0b1);
}
public set(index: number, value: boolean) {
this.#ensureValidIndex(index);
const byteIndex = getByteIndex(index);
this.#bits[byteIndex] = setBit(this.#bits[byteIndex], getBitIndex(index), value);
}
public isFull() {
return this.#bits.subarray(0, this.#wholeBytes).every(byte => byte === FULL_BYTE) && (this.#remainingBits === 0 || this.#bits[this.#wholeBytes] === this.#remainingBitsMask);
}
public isEmpty() {
return this.#bits.every(byte => byte === 0);
}
public fill(value: boolean) {
const b = value ? FULL_BYTE : 0;
for (let i = 0; i < this.#wholeBytes; i++) {
this.#bits[i] = b;
}
if (this.#remainingBits > 0) {
this.#bits[this.#wholeBytes] = value ? this.#remainingBitsMask : 0;
}
}
public and(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.#bits[i] &= other.#bits[i];
}
return this;
}
public or(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.#bits[i] |= other.#bits[i];
}
return this;
}
public xor(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.#bits[i] ^= other.#bits[i];
}
return this;
}
public not() {
for (let i = 0; i < this.#wholeBytes; i++) {
this.#bits[i] = ~this.#bits[i] & FULL_BYTE;
}
if (this.#remainingBits > 0) {
this.#bits[this.#wholeBytes] = ~this.#bits[this.#wholeBytes] & this.#remainingBitsMask;
}
return this;
}
public andNot(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.#bits[i] &= ~other.#bits[i];
}
return this;
}
public contains(other: BitArray) {
this.#ensureSameSize(other);
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b);
}
public intersects(other: BitArray) {
this.#ensureSameSize(other);
return arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0);
}
public copy() {
const copy = new BitArray(this.#length);
copy.#bits.set(this.#bits);
return copy;
}
public toArray() {
return sequence(this).toArray();
}
public equals(other: BitArray) {
return other === this || isDefined(other) && arrayLike(this.#bits).equals(arrayLike(other.#bits));
}
public toString() {
return sequence(this).select(bit => bit ? '1' : '0').collect(join());
}
}

474
src/bitarray/impl.ts Normal file
View File

@@ -0,0 +1,474 @@
import { join } from "../collector/index.js";
import { arrayLike, sequence } from "../sync/index.js";
import { emptyIterableIterator } from "../utils.js";
import { BitArray } from "./types.js";
const BYTE_SIZE = Uint8Array.BYTES_PER_ELEMENT * 8;
const FULL_BYTE = getMask(BYTE_SIZE);
function getMask(bitCount: number) {
return ~(~0b0 << bitCount);
}
function getByteIndex(index: number) {
return Math.floor(index / BYTE_SIZE);
}
function getBitIndex(index: number) {
return index % BYTE_SIZE;
}
function setBit(byte: number, bitIndex: number, value: boolean) {
const mask = 0b1 << bitIndex;
return value ? (byte | mask) : (byte & ~mask);
}
function* yieldBits(byte: number, bitCount: number) {
for (let i = 0; i < bitCount; i++) {
yield Boolean((byte >>> i) & 0b1);
}
}
function* getBytes(bits: Iterable<boolean>) {
const bitIterator = bits[Symbol.iterator]();
while (true) {
let next = bitIterator.next();
if (next.done) {
break;
}
let byte = 0;
for (let i = 0; i < BYTE_SIZE; i++) {
byte |= Number(next.value) << i;
next = bitIterator.next();
if (next.done) {
break;
}
}
yield byte;
}
}
export class BitArrayImpl implements BitArray {
readonly #length: number;
readonly #bits: Uint8Array;
readonly #wholeBytes: number;
readonly #remainingBits: number;
readonly #remainingBitsMask: number;
public constructor(length: number) {
const exactBytes = length / BYTE_SIZE;
const remainingBits = length % BYTE_SIZE;
this.#length = length;
this.#bits = new Uint8Array(Math.ceil(exactBytes));
this.#wholeBytes = Math.floor(exactBytes);
this.#remainingBits = remainingBits;
this.#remainingBitsMask = getMask(remainingBits);
}
*[Symbol.iterator]() {
for (let i = 0; i < this.#wholeBytes; i++) {
yield* yieldBits(this.#bits[i], BYTE_SIZE);
}
if (this.#remainingBits > 0) {
yield* yieldBits(this.#bits[this.#wholeBytes], this.#remainingBits);
}
}
#ensureValidIndex(index: number) {
if (index < 0 || index >= this.#length) {
throw new RangeError("The index is outside the BitArray range.");
}
}
#ensureSameSize(other: BitArray) {
if (this.#length !== other.length) {
throw new TypeError("The BitArrays do not have the same length.");
}
}
public get length() {
return this.#length;
}
public get(index: number) {
this.#ensureValidIndex(index);
return Boolean((this.#bits[getByteIndex(index)] >>> getBitIndex(index)) & 0b1);
}
public set(index: number, value: boolean) {
this.#ensureValidIndex(index);
const byteIndex = getByteIndex(index);
this.#bits[byteIndex] = setBit(this.#bits[byteIndex], getBitIndex(index), value);
}
public isFull() {
return this.#bits.subarray(0, this.#wholeBytes).every(byte => byte === FULL_BYTE) && (this.#remainingBits === 0 || this.#bits[this.#wholeBytes] === this.#remainingBitsMask);
}
public isEmpty() {
return this.#bits.every(byte => byte === 0);
}
public fill(value: boolean) {
const b = value ? FULL_BYTE : 0;
for (let i = 0; i < this.#wholeBytes; i++) {
this.#bits[i] = b;
}
if (this.#remainingBits > 0) {
this.#bits[this.#wholeBytes] = value ? this.#remainingBitsMask : 0;
}
}
public and(other: BitArray) {
this.#ensureSameSize(other);
if (other instanceof BitArrayImpl) {
for (let i = 0; i < this.#length; i++) {
this.#bits[i] &= other.#bits[i];
}
} else {
let i = 0;
for (const byte of getBytes(other)) {
this.#bits[i++] &= byte;
}
}
return this;
}
public or(other: BitArray) {
this.#ensureSameSize(other);
if (other instanceof BitArrayImpl) {
for (let i = 0; i < this.#length; i++) {
this.#bits[i] |= other.#bits[i];
}
} else {
let i = 0;
for (const byte of getBytes(other)) {
this.#bits[i++] |= byte;
}
}
return this;
}
public xor(other: BitArray) {
this.#ensureSameSize(other);
if (other instanceof BitArrayImpl) {
for (let i = 0; i < this.#length; i++) {
this.#bits[i] ^= other.#bits[i];
}
} else {
let i = 0;
for (const byte of getBytes(other)) {
this.#bits[i++] ^= byte;
}
}
return this;
}
public not() {
for (let i = 0; i < this.#wholeBytes; i++) {
this.#bits[i] = ~this.#bits[i] & FULL_BYTE;
}
if (this.#remainingBits > 0) {
this.#bits[this.#wholeBytes] = ~this.#bits[this.#wholeBytes] & this.#remainingBitsMask;
}
return this;
}
public contains(other: BitArray) {
this.#ensureSameSize(other);
return other instanceof BitArrayImpl ?
arrayLike(this.#bits).zip(arrayLike(other.#bits)).all(([a, b]) => (a & b) === b) :
sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a]) => a);
}
public intersects(other: BitArray) {
this.#ensureSameSize(other);
return other instanceof BitArrayImpl ?
arrayLike(this.#bits).zip(arrayLike(other.#bits)).any(([a, b]) => (a & b) !== 0) :
sequence(this).zip(sequence(other)).any(([a, b]) => a && b);
}
public slice(offset: number, length: number) {
return new BitArraySlice(this, offset, length);
}
public copy() {
const copy = new BitArrayImpl(this.#length);
copy.#bits.set(this.#bits);
return copy;
}
public toArray() {
return sequence(this).toArray();
}
public equals(other: BitArray) {
return other === this || other && (other instanceof BitArrayImpl ? arrayLike(this.#bits).sequenceEquals(arrayLike(other.#bits)) : sequence(this).sequenceEquals(sequence(other)));
}
public toString() {
return sequence(this).select(bit => bit ? '1' : '0').collect(join());
}
}
export class EmptyBitArray implements BitArray {
[Symbol.iterator](): Iterator<boolean, any, undefined> {
return emptyIterableIterator<boolean>();
}
get length() {
return 0;
}
isFull() {
return false;
}
isEmpty() {
return false;
}
get(index: number): boolean {
throw new Error("BitArray has zero length");
}
set(index: number, value: boolean) {
throw new Error("BitArray has zero length");
}
fill(value: boolean) { }
and(other: BitArray) {
if (other.length !== 0) {
throw new Error("The other BitArray does not have zero length");
}
return this;
}
or(other: BitArray) {
if (other.length !== 0) {
throw new Error("The other BitArray does not have zero length");
}
return this;
}
xor(other: BitArray) {
if (other.length !== 0) {
throw new Error("The other BitArray does not have zero length");
}
return this;
}
not() {
return this;
}
contains(other: BitArray) {
return false;
}
intersects(other: BitArray) {
return false;
}
slice(offset: number, length: number) {
if (offset > 0) {
throw new Error("offset > 0");
}
if (length > 0) {
throw new Error("length > 0");
}
return this;
}
copy() {
return this;
}
toArray(): boolean[] {
return [];
}
equals(other: BitArray) {
return other === this || other && other.length === 0;
}
toString() {
return "";
}
}
export class BitArraySlice implements BitArray {
readonly #parent: BitArray;
readonly #offset: number;
readonly #length: number;
public constructor(parent: BitArray, offset: number, length: number) {
this.#parent = parent;
this.#offset = offset;
this.#length = length;
}
*[Symbol.iterator]() {
for (let i = 0; i < this.#length; i++) {
yield this.#parent.get(i + this.#offset);
}
}
#ensureValidIndex(index: number) {
if (index < 0 || index >= this.#length) {
throw new RangeError("The index is outside the BitArray range.");
}
}
#ensureSameSize(other: BitArray) {
if (this.#length !== other.length) {
throw new TypeError("The BitArrays do not have the same length.");
}
}
public get length() {
return this.#length;
}
public isFull() {
for (const bit of this) {
if (!bit) {
return false;
}
}
return true;
}
public isEmpty() {
for (const bit of this) {
if (bit) {
return false;
}
}
return true;
}
public get(index: number) {
this.#ensureValidIndex(index);
return this.#parent.get(index + this.#offset);
}
public set(index: number, value: boolean) {
this.#ensureValidIndex(index);
this.#parent.set(index + this.#offset, value);
}
public fill(value: boolean) {
for (let i = 0; i < this.#length; i++) {
this.#parent.set(i + this.#offset, value);
}
}
public and(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.set(i, this.get(i) && other.get(i));
}
return this;
}
public or(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.set(i, this.get(i) || other.get(i));
}
return this;
}
public xor(other: BitArray) {
this.#ensureSameSize(other);
for (let i = 0; i < this.#length; i++) {
this.set(i, this.get(i) !== other.get(i));
}
return this;
}
public not() {
for (let i = 0; i < this.#length; i++) {
this.set(i, !this.get(i));
}
return this;
}
public contains(other: BitArray) {
this.#ensureSameSize(other);
return sequence(this).zip(sequence(other)).where(([, b]) => b).all(([a, b]) => a && b);
}
public intersects(other: BitArray) {
this.#ensureSameSize(other);
return sequence(this).zip(sequence(other)).any(([a, b]) => a && b);
}
public slice(offset: number, length: number) {
return new BitArraySlice(this.#parent, offset + this.#offset, length);
}
public copy() {
const copy = new BitArrayImpl(this.#length);
for (let i = 0; i < this.#length; i++) {
copy.set(i, this.get(i));
}
return copy;
}
public toArray() {
return sequence(this).toArray();
}
public equals(other: BitArray) {
return other === this || other && sequence(this).sequenceEquals(sequence(other));
}
public toString() {
return sequence(this).select(bit => bit ? '1' : '0').collect(join());
}
}

48
src/bitarray/index.ts Normal file
View File

@@ -0,0 +1,48 @@
import { asArray } from "../utils.js";
import { EmptyBitArray, BitArrayImpl } from "./impl.js";
import { BitArray } from "./types.js";
namespace BitArray {
export const EMPTY: BitArray = new EmptyBitArray();
export function create(length: number): BitArray {
if (length < 0) {
throw new Error("length < 0");
}
return length === 0 ? EMPTY : new BitArrayImpl(length);
}
export function from(bits: Iterable<boolean>) {
const arr = asArray(bits);
const result = create(arr.length);
for (let i = 0; i < arr.length; i++) {
result.set(i, arr[i]);
}
return result;
}
export function of(...bits: boolean[]) {
return from(bits);
}
export function and(a: BitArray, b: BitArray) {
return a.copy().and(b);
}
export function or(a: BitArray, b: BitArray) {
return a.copy().or(b);
}
export function xor(a: BitArray, b: BitArray) {
return a.copy().xor(b);
}
export function not(a: BitArray) {
return a.copy().not();
}
}
export { BitArray };

26
src/bitarray/types.ts Normal file
View File

@@ -0,0 +1,26 @@
export interface BitArray extends Iterable<boolean> {
readonly length: number;
isFull(): boolean;
isEmpty(): boolean;
get(index: number): boolean;
set(index: number, value: boolean): void;
fill(value: boolean): void;
and(other: BitArray): BitArray;
or(other: BitArray): BitArray;
xor(other: BitArray): BitArray;
not(): BitArray;
contains(other: BitArray): boolean;
intersects(other: BitArray): boolean;
slice(offset: number, length: number): BitArray;
copy(): BitArray;
toArray(): boolean[];
equals(other: BitArray): boolean;
toString(): string;
}

View File

@@ -1,245 +0,0 @@
import { Converter } from "./sync.js";
export interface Collector<TElement, TAccumulator, TResult> {
initialize(): TAccumulator;
accumulate(accumulator: TAccumulator, element: TElement): void;
finalize(accumulator: TAccumulator): TResult;
}
class SimpleCollector<TElement, TAccumulator, TResult> implements Collector<TElement, TAccumulator, TResult> {
readonly #initialize: () => TAccumulator;
readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void;
readonly #finalize: (accumulator: TAccumulator) => TResult;
constructor(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult) {
this.#initialize = initialize;
this.#accumulate = accumulate;
this.#finalize = finalize;
}
initialize() {
return this.#initialize();
}
accumulate(accumulator: TAccumulator, element: TElement) {
this.#accumulate(accumulator, element);
}
finalize(accumulator: TAccumulator): TResult {
return this.#finalize(accumulator);
}
}
export function create<TElement, TAccumulator, TResult>(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult): Collector<TElement, TAccumulator, TResult> {
return new SimpleCollector(initialize, accumulate, finalize);
}
class ToArrayCollector<TElement> implements Collector<TElement, TElement[], TElement[]> {
initialize(): TElement[] {
return [];
}
accumulate(accumulator: TElement[], element: TElement) {
accumulator.push(element);
}
finalize(accumulator: TElement[]) {
return accumulator;
}
}
const toArrayCollector = new ToArrayCollector<any>();
export function toArray<TElement>(): Collector<TElement, any, TElement[]> {
return toArrayCollector;
}
class ToObjectCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue>, Record<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return {} as Record<TKey, TValue>;
}
accumulate(accumulator: Record<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator[key] = value;
}
finalize(accumulator: Record<TKey, TValue>) {
return accumulator;
}
}
export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue>> {
return new ToObjectCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToMapCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue>, Map<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return new Map<TKey, TValue>();
}
accumulate(accumulator: Map<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator.set(key, value);
}
finalize(accumulator: Map<TKey, TValue>) {
return accumulator;
}
}
export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue>> {
return new ToMapCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
class ToSetCollector<TElement> implements Collector<TElement, Set<TElement>, Set<TElement>> {
initialize() {
return new Set<TElement>();
}
accumulate(accumulator: Set<TElement>, element: TElement) {
accumulator.add(element);
}
finalize(accumulator: Set<TElement>) {
return accumulator;
}
}
const toSetCollector = new ToSetCollector<any>();
export function toSet<TElement>(): Collector<TElement, any, Set<TElement>> {
return toSetCollector;
}
class JoinCollector implements Collector<any, any[], string> {
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 ?? "";
}
initialize(): any[] {
return [];
}
accumulate(accumulator: any[], element: string) {
accumulator.push(element);
}
finalize(accumulator: any[]) {
return this.#prefix + accumulator.join(this.#delimiter) + this.#suffix;
}
}
export function join(delimiter?: string, prefix?: string, suffix?: string): Collector<any, any, string> {
return new JoinCollector(delimiter, prefix, suffix);
}
class SumCollector implements Collector<number, { sum: number }, number> {
initialize() {
return { sum: 0 };
}
accumulate(accumulator: { sum: number }, element: number) {
accumulator.sum += element;
}
finalize(accumulator: { sum: number }) {
return accumulator.sum;
}
}
const sumCollector = new SumCollector();
export function sum(): Collector<number, any, number> {
return sumCollector;
}
class BigIntSumCollector implements Collector<bigint, { sum: bigint }, bigint> {
initialize() {
return { sum: 0n, };
}
accumulate(accumulator: { sum: bigint }, element: bigint) {
accumulator.sum += element;
}
finalize(accumulator: { sum: bigint }) {
return accumulator.sum;
}
}
const bigintSumCollector = new BigIntSumCollector();
export function bigintSum(): Collector<bigint, any, bigint> {
return bigintSumCollector;
}
class AverageCollector implements Collector<number, { count: number, sum: number }, number> {
initialize() {
return { count: 0, sum: 0 };
}
accumulate(accumulator: { count: number, sum: number }, element: number) {
accumulator.count++;
accumulator.sum += element;
}
finalize(accumulator: { count: number, sum: number }) {
return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count;
}
}
const averageCollector = new AverageCollector();
export function average(): Collector<number, any, number> {
return averageCollector;
}
class BigIntAverageCollector implements Collector<bigint, { count: number, sum: bigint }, bigint> {
initialize() {
return { count: 0, sum: 0n };
}
accumulate(accumulator: { count: number, sum: bigint }, element: bigint) {
accumulator.count++;
accumulator.sum += element;
}
finalize(accumulator: { count: number, sum: bigint }) {
return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count);
}
}
const bigintAverageCollector = new BigIntAverageCollector();
export function bigintAverage(): Collector<bigint, any, bigint> {
return bigintAverageCollector;
}

249
src/collector/impl.ts Normal file
View File

@@ -0,0 +1,249 @@
import { Converter } from "../types.js";
import { Collector } from "./types.js";
export class SimpleCollector<TElement, TAccumulator, TResult> implements Collector<TElement, TAccumulator, TResult> {
readonly #initialize: () => TAccumulator;
readonly #accumulate: (accumulator: TAccumulator, element: TElement) => void;
readonly #finalize: (accumulator: TAccumulator) => TResult;
constructor(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult) {
this.#initialize = initialize;
this.#accumulate = accumulate;
this.#finalize = finalize;
}
initialize() {
return this.#initialize();
}
accumulate(accumulator: TAccumulator, element: TElement) {
this.#accumulate(accumulator, element);
}
finalize(accumulator: TAccumulator): TResult {
return this.#finalize(accumulator);
}
}
export class ToArrayCollector<TElement> implements Collector<TElement, TElement[], TElement[]> {
initialize(): TElement[] {
return [];
}
accumulate(accumulator: TElement[], element: TElement) {
accumulator.push(element);
}
finalize(accumulator: TElement[]) {
return accumulator;
}
}
export class ToObjectCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue>, Record<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return {} as Record<TKey, TValue>;
}
accumulate(accumulator: Record<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator[key] = value;
}
finalize(accumulator: Record<TKey, TValue>) {
return accumulator;
}
}
export class ToObjectListCollector<TElement, TKey extends PropertyKey, TValue> implements Collector<TElement, Record<TKey, TValue[]>, Record<TKey, TValue[]>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return {} as Record<TKey, TValue[]>;
}
accumulate(accumulator: Record<TKey, TValue[]>, 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<TKey, TValue[]>) {
return accumulator;
}
}
export class ToMapCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue>, Map<TKey, TValue>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return new Map<TKey, TValue>();
}
accumulate(accumulator: Map<TKey, TValue>, element: TElement) {
const key = this.#keySelector(element);
const value = this.#valueSelector(element);
accumulator.set(key, value);
}
finalize(accumulator: Map<TKey, TValue>) {
return accumulator;
}
}
export class ToMapListCollector<TElement, TKey, TValue> implements Collector<TElement, Map<TKey, TValue[]>, Map<TKey, TValue[]>> {
readonly #keySelector: Converter<TElement, TKey>;
readonly #valueSelector: Converter<TElement, TValue>;
constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
this.#keySelector = keySelector;
this.#valueSelector = valueSelector;
}
initialize() {
return new Map<TKey, TValue[]>();
}
accumulate(accumulator: Map<TKey, TValue[]>, 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<TKey, TValue[]>) {
return accumulator;
}
}
export class ToSetCollector<TElement> implements Collector<TElement, Set<TElement>, Set<TElement>> {
initialize() {
return new Set<TElement>();
}
accumulate(accumulator: Set<TElement>, element: TElement) {
accumulator.add(element);
}
finalize(accumulator: Set<TElement>) {
return accumulator;
}
}
export class JoinCollector implements Collector<any, any[], string> {
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 ?? "";
}
initialize(): any[] {
return [];
}
accumulate(accumulator: any[], element: string) {
accumulator.push(element);
}
finalize(accumulator: any[]) {
return this.#prefix + accumulator.join(this.#delimiter) + this.#suffix;
}
}
export class SumCollector implements Collector<number, { sum: number; }, number> {
initialize() {
return { sum: 0 };
}
accumulate(accumulator: { sum: number; }, element: number) {
accumulator.sum += element;
}
finalize(accumulator: { sum: number; }) {
return accumulator.sum;
}
}
export class BigIntSumCollector implements Collector<bigint, { sum: bigint; }, bigint> {
initialize() {
return { sum: 0n, };
}
accumulate(accumulator: { sum: bigint; }, element: bigint) {
accumulator.sum += element;
}
finalize(accumulator: { sum: bigint; }) {
return accumulator.sum;
}
}
export class AverageCollector implements Collector<number, { count: number, sum: number; }, number> {
initialize() {
return { count: 0, sum: 0 };
}
accumulate(accumulator: { count: number, sum: number; }, element: number) {
accumulator.count++;
accumulator.sum += element;
}
finalize(accumulator: { count: number, sum: number; }) {
return accumulator.count === 0 ? 0 : accumulator.sum / accumulator.count;
}
}
export class BigIntAverageCollector implements Collector<bigint, { count: number, sum: bigint; }, bigint> {
initialize() {
return { count: 0, sum: 0n };
}
accumulate(accumulator: { count: number, sum: bigint; }, element: bigint) {
accumulator.count++;
accumulator.sum += element;
}
finalize(accumulator: { count: number, sum: bigint; }) {
return accumulator.count === 0 ? 0n : accumulator.sum / BigInt(accumulator.count);
}
}

64
src/collector/index.ts Normal file
View File

@@ -0,0 +1,64 @@
import { Converter } from "../types.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<TElement, TAccumulator, TResult>(initialize: () => TAccumulator, accumulate: (accumulator: TAccumulator, element: TElement) => void,
finalize: (accumulator: TAccumulator) => TResult): Collector<TElement, TAccumulator, TResult> {
return new SimpleCollector(initialize, accumulate, finalize);
}
const toArrayCollector = new ToArrayCollector<any>();
export function toArray<TElement>(): Collector<TElement, any, TElement[]> {
return toArrayCollector;
}
export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue>> {
return new ToObjectCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
export function toObjectList<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Record<TKey, TValue[]>> {
return new ToObjectListCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue>> {
return new ToMapCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
export function toMapList<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Collector<TElement, any, Map<TKey, TValue[]>> {
return new ToMapListCollector<TElement, TKey, TValue>(keySelector, valueSelector);
}
const toSetCollector = new ToSetCollector<any>();
export function toSet<TElement>(): Collector<TElement, any, Set<TElement>> {
return toSetCollector;
}
export function join(delimiter?: string, prefix?: string, suffix?: string): Collector<any, any, string> {
return new JoinCollector(delimiter, prefix, suffix);
}
const sumCollector = new SumCollector();
export function sum(): Collector<number, any, number> {
return sumCollector;
}
const bigintSumCollector = new BigIntSumCollector();
export function bigintSum(): Collector<bigint, any, bigint> {
return bigintSumCollector;
}
const averageCollector = new AverageCollector();
export function average(): Collector<number, any, number> {
return averageCollector;
}
const bigintAverageCollector = new BigIntAverageCollector();
export function bigintAverage(): Collector<bigint, any, bigint> {
return bigintAverageCollector;
}

5
src/collector/types.ts Normal file
View File

@@ -0,0 +1,5 @@
export interface Collector<TElement, TAccumulator, TResult> {
initialize(): TAccumulator;
accumulate(accumulator: TAccumulator, element: TElement): void;
finalize(accumulator: TAccumulator): TResult;
}

219
src/collector/v2/impl.ts Normal file
View File

@@ -0,0 +1,219 @@
// import { Converter } from "../../types.js";
// import { CollectorFactory, CollectorInstance } from "./types.js";
// export class ToArrayCollectorFactory<TElement> implements CollectorFactory<TElement, TElement[]> {
// initialize() {
// return new ToArrayCollectorInstance<TElement>();
// }
// };
// class ToArrayCollectorInstance<TElement> implements CollectorInstance<TElement, TElement[]> {
// readonly #accumulator: TElement[];
// constructor() {
// this.#accumulator = [];
// }
// accumulate(element: TElement) {
// this.#accumulator.push(element);
// }
// finalize() {
// return this.#accumulator;
// }
// }
// export class ToObjectCollectorFactory<TElement, TKey extends PropertyKey, TValue> implements CollectorFactory<TElement, Record<TKey, TValue>> {
// readonly #keySelector: Converter<TElement, TKey>;
// readonly #valueSelector: Converter<TElement, TValue>;
// constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
// this.#keySelector = keySelector;
// this.#valueSelector = valueSelector;
// }
// get keySelector() {
// return this.#keySelector;
// }
// get valueSelector() {
// return this.#valueSelector;
// }
// initialize() {
// return new ToObjectCollectorInstance<TElement, TKey, TValue>(this);
// }
// }
// class ToObjectCollectorInstance<TElement, TKey extends PropertyKey, TValue> implements CollectorInstance<TElement, Record<TKey, TValue>> {
// readonly #factory: ToObjectCollectorFactory<TElement, TKey, TValue>;
// readonly #accumulator = <Record<TKey, TValue>>{};
// constructor(factory: ToObjectCollectorFactory<TElement, TKey, TValue>) {
// 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<TElement, TKey extends PropertyKey, TValue> implements CollectorFactory<TElement, Map<TKey, TValue>> {
// readonly #keySelector: Converter<TElement, TKey>;
// readonly #valueSelector: Converter<TElement, TValue>;
// constructor(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>) {
// this.#keySelector = keySelector;
// this.#valueSelector = valueSelector;
// }
// get keySelector() {
// return this.#keySelector;
// }
// get valueSelector() {
// return this.#valueSelector;
// }
// initialize() {
// return new ToMapCollectorInstance<TElement, TKey, TValue>(this);
// }
// }
// class ToMapCollectorInstance<TElement, TKey extends PropertyKey, TValue> implements CollectorInstance<TElement, Map<TKey, TValue>> {
// readonly #factory: ToMapCollectorFactory<TElement, TKey, TValue>;
// readonly #accumulator = new Map<TKey, TValue>();
// constructor(factory: ToMapCollectorFactory<TElement, TKey, TValue>) {
// 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<TElement> implements CollectorFactory<TElement, Set<TElement>> {
// initialize() {
// return new ToSetCollectorInstance<TElement>();
// }
// }
// class ToSetCollectorInstance<TElement> implements CollectorInstance<TElement, Set<TElement>> {
// readonly #accumulator = new Set<TElement>();
// accumulate(element: TElement) {
// this.#accumulator.add(element);
// }
// finalize() {
// return this.#accumulator;
// }
// }
// export class JoinCollectorFactory implements CollectorFactory<any, string> {
// 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<any, string> {
// 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<number, number> {
// #accumulator = 0;
// accumulate(element: number) {
// this.#accumulator += element;
// }
// finalize() {
// return this.#accumulator;
// }
// }
// export class BigIntSumCollectorInstance implements CollectorInstance<bigint, bigint> {
// #accumulator = 0n;
// accumulate(element: bigint) {
// this.#accumulator += element;
// }
// finalize() {
// return this.#accumulator;
// }
// }
// export class AverageCollectorInstance implements CollectorInstance<number, number> {
// #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<bigint, bigint> {
// #count = 0n;
// #sum = 0n;
// accumulate(element: bigint) {
// this.#count++;
// this.#sum += element;
// }
// finalize() {
// return this.#count === 0n ? 0n : this.#sum / this.#count;
// }
// }

57
src/collector/v2/index.ts Normal file
View File

@@ -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<any>();
// export function toArray<TElement>(): CollectorFactory<TElement, TElement[]> {
// return toArrayCollectorFactory;
// }
// export function toObject<TElement, TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): CollectorFactory<TElement, Record<TKey, TValue>> {
// return () => toObjectCollectorFactory<TElement, TKey, TValue>(keySelector, valueSelector);
// }
// export function toMap<TElement, TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): CollectorFactory<TElement, Map<TKey, TValue>> {
// return () => new ToMapCollectorInstance<TElement, TKey, TValue>(keySelector, valueSelector);
// }
// export function toSet<TElement>(): CollectorFactory<TElement, Set<TElement>> {
// return () => new ToSetCollectorInstance<TElement>();
// }
// export function join(delimiter?: string, prefix?: string, suffix?: string): CollectorFactory<any, string> {
// return () => new JoinCollectorInstance(delimiter, prefix, suffix);
// }
// function sumCollectorFactory() {
// return new SumCollectorInstance();
// }
// export function sum(): CollectorFactory<number, number> {
// return sumCollectorFactory;
// }
// function bigintSumCollectorFactory() {
// return new BigIntSumCollectorInstance();
// }
// export function bigintSum(): CollectorFactory<bigint | number, bigint> {
// return bigintSumCollectorFactory;
// }
// function averageCollectorFactory() {
// return new AverageCollectorInstance();
// }
// export function average(): CollectorFactory<number, number> {
// return averageCollectorFactory;
// }
// function bigintAverageCollectorFactory() {
// return new BigIntAverageCollectorInstance();
// }
// export function bigintAverage(): CollectorFactory<bigint, bigint> {
// return bigintAverageCollectorFactory;
// }

View File

@@ -0,0 +1,8 @@
// export interface CollectorFactory<TElement, TResult> {
// initialize(): CollectorInstance<TElement, TResult>;
// }
// export interface CollectorInstance<TElement, TResult> {
// accumulate(element: TElement): void;
// finalize(): TResult;
// }

263
src/comparer/async.ts Normal file
View File

@@ -0,0 +1,263 @@
import { MaybeAsyncConverter } from "../types.js";
import { Nullable } from "../utils.js";
import { AsyncComparer, AsyncComparison, Comparer, MaybeAsyncComparison, MaybeAsyncComparisonOrComparer } from "./types.js";
export function isAsyncComparer<T>(obj: any): obj is AsyncComparer<T> {
return obj instanceof BaseAsyncComparer;
}
export function asAsyncComparer<T>(comparer: MaybeAsyncComparisonOrComparer<T>): AsyncComparer<T> {
return typeof comparer === "function" ? createAsyncComparer(comparer) : isAsyncComparer<T>(comparer) ? comparer : fromSyncComparer(comparer);
}
export function fromSyncComparer<T>(comparer: Comparer<T>): AsyncComparer<T> {
return new WrappedAsyncComparer(comparer);
}
export function createAsyncComparer<T>(comparison: MaybeAsyncComparison<T>): AsyncComparer<T> {
return new SimpleAsyncComparer(comparison);
}
export function createAsyncComparerUsing<T, U>(projection: MaybeAsyncConverter<T, U>, comparison?: MaybeAsyncComparisonOrComparer<U>): AsyncComparer<T> {
return new MappedAsyncComparer(projection, comparison);
}
export function combineAsyncComparers<T>(comparers: Iterable<Nullable<MaybeAsyncComparisonOrComparer<T>>>): AsyncComparer<T> | undefined {
let result: AsyncComparer<T> = dummyAsyncComparer;
for (const comparer of comparers) {
if (!comparer) {
continue;
}
result = result.then(asAsyncComparer(comparer));
}
return result === dummyAsyncComparer ? undefined : result;
}
export abstract class BaseAsyncComparer<T> implements AsyncComparer<T> {
#cachedBoundComparison: AsyncComparison<T> | undefined;
public abstract compare(a: T, b: T): Promise<number>;
public comparison(): AsyncComparison<T> {
return this.#cachedBoundComparison ??= this.compare.bind(this);
}
public reverse(): AsyncComparer<T> {
return new ReversedAsyncComparer(this);
}
public then(comparer: AsyncComparer<T>): AsyncComparer<T> {
return new ThenAsyncComparer(this, comparer);
}
public thenCompare(comparison: MaybeAsyncComparison<T>): AsyncComparer<T> {
return this.then(createAsyncComparer(comparison));
}
public thenCompareUsing<U>(projection: MaybeAsyncConverter<T, U>, comparer?: MaybeAsyncComparisonOrComparer<U>): AsyncComparer<T> {
return this.then(createAsyncComparerUsing(projection, comparer));
}
public nullAwareComparer(): AsyncComparer<Nullable<T>> {
return new NullAwareAsyncComparer(this);
}
}
class WrappedAsyncComparer<T> extends BaseAsyncComparer<T> {
readonly #base: Comparer<T>;
constructor(base: Comparer<T>) {
super();
this.#base = base;
}
public override async compare(a: T, b: T) {
return this.#base.compare(a, b);
}
}
class SimpleAsyncComparer<T> extends BaseAsyncComparer<T> {
readonly #comparison: MaybeAsyncComparison<T>;
public constructor(comparison: MaybeAsyncComparison<T>) {
super();
this.#comparison = comparison;
}
public async compare(a: T, b: T) {
return await this.#comparison(a, b);
}
}
class MappedAsyncComparer<T, U> extends BaseAsyncComparer<T> {
readonly #projection: MaybeAsyncConverter<T, U>;
readonly #comparer: AsyncComparer<U>;
public constructor(projection: MaybeAsyncConverter<T, U>, comparer?: MaybeAsyncComparisonOrComparer<U>) {
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<T> extends BaseAsyncComparer<T> {
readonly #base: AsyncComparer<T>;
public constructor(base: AsyncComparer<T>) {
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<T> extends BaseAsyncComparer<T> {
readonly #base: AsyncComparer<T>;
readonly #comparer: AsyncComparer<T>;
public constructor(base: AsyncComparer<T>, comparer: AsyncComparer<T>) {
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);
}
}
const dummyAsyncComparer = new class DummyAsyncComparer extends BaseAsyncComparer<any> {
public override async compare(_a: any, _b: any): Promise<number> {
return 0;
}
public override then(comparer: AsyncComparer<any>): AsyncComparer<any> {
return comparer;
}
public override thenCompare(comparison: MaybeAsyncComparison<any>): AsyncComparer<any> {
return createAsyncComparer(comparison);
}
public override thenCompareUsing<U>(projection: MaybeAsyncConverter<any, U>, comparison?: MaybeAsyncComparisonOrComparer<U> | undefined): AsyncComparer<any> {
return createAsyncComparerUsing(projection, comparison);
}
}
export const defaultAsyncComparer: AsyncComparer<any> = new class DefaultAsyncComparer extends BaseAsyncComparer<any> {
public override async compare(a: any, b: any): Promise<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 getDefaultAsyncComparer<T>(): AsyncComparer<T> {
return defaultAsyncComparer;
}
class NullAwareAsyncComparer<T> extends BaseAsyncComparer<Nullable<T>> {
readonly #base: AsyncComparer<T>;
constructor(baseComparer: AsyncComparer<T>) {
super();
this.#base = baseComparer;
}
public override async compare(a: Nullable<T>, b: Nullable<T>): Promise<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 await this.#base.compare(a, b);
}
public override nullAwareComparer(): AsyncComparer<Nullable<T>> {
return this;
}
}
export function nullAwareComparer<T>(baseComparer: AsyncComparer<T>): AsyncComparer<Nullable<T>> {
return new NullAwareAsyncComparer(baseComparer);
}
export const stringAsyncComparer: AsyncComparer<string> = new class StringAsyncComparer extends BaseAsyncComparer<string> {
public override async compare(a: string, b: string): Promise<number> {
return a.localeCompare(b);
}
};
export const numberAsyncComparer: AsyncComparer<number> = new class NumberAsyncComparer extends BaseAsyncComparer<number> {
public override async compare(a: number, b: number): Promise<number> {
return a - b;
}
};
export const bigintAsyncComparer: AsyncComparer<bigint> = new class BigIntAsyncComparer extends BaseAsyncComparer<bigint> {
public override async compare(a: bigint, b: bigint): Promise<number> {
return Number(a - b);
}
};
export const booleanAsyncComparer: AsyncComparer<boolean> = new class BooleanAsyncComparer extends BaseAsyncComparer<boolean> {
public override async compare(a: boolean, b: boolean): Promise<number> {
return Number(a) - Number(b);
}
};
export const dateAsyncComparer: AsyncComparer<Date> = new class DateAsyncComparer extends BaseAsyncComparer<Date> {
public override async compare(a: Date, b: Date): Promise<number> {
return a.getTime() - b.getTime();
}
};

262
src/comparer/sync.ts Normal file
View File

@@ -0,0 +1,262 @@
import { Converter } from "../types.js";
import { Nullable } from "../utils.js";
import { fromSyncComparer } from "./async.js";
import { Comparer, ComparisonOrComparer, Comparison, AsyncComparer } from "./types.js";
export function isComparer<T>(obj: any): obj is Comparer<T> {
return obj instanceof BaseComparer;
}
export function asComparer<T>(comparer: ComparisonOrComparer<T>) {
return typeof comparer === "function" ? createComparer(comparer) : comparer;
}
export function asComparison<T>(comparer: ComparisonOrComparer<T>) {
return typeof comparer === "function" ? comparer : comparer.comparison();
}
export function createComparer<T = any>(comparison: Comparison<T>): Comparer<T> {
return new SimpleComparer(comparison);
}
export function createComparerUsing<T = any, U = any>(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>): Comparer<T> {
return new MappedComparer(projection, comparison);
}
export function reverseComparison<T>(comparison: Comparison<T>): Comparison<T> {
return (a, b) => comparison(b, a);
}
export function combineComparers<T>(comparers: Iterable<Nullable<ComparisonOrComparer<T>>>) {
let result: Comparer<T> = dummyComparer;
for (const comparer of comparers) {
if (!comparer) {
continue;
}
result = result.then(asComparer(comparer));
}
return result === dummyComparer ? undefined : result;
}
export abstract class BaseComparer<T> implements Comparer<T> {
#cachedBoundComparison: Comparison<T> | undefined;
public abstract compare(a: T, b: T): number;
public comparison(): Comparison<T> {
return this.#cachedBoundComparison ??= this.compare.bind(this);
}
public reverse(): Comparer<T> {
return new ReversedComparer(this);
}
public then(comparer: Comparer<T>): Comparer<T> {
return new ThenComparer(this, comparer);
}
public thenCompare(comparison: Comparison<T>): Comparer<T> {
return this.then(createComparer(comparison));
}
public thenCompareUsing<U>(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>): Comparer<T> {
return this.then(createComparerUsing(projection, comparison));
}
public toAsync(): AsyncComparer<T> {
return fromSyncComparer<T>(this);
}
public nullAwareComparer(): Comparer<Nullable<T>> {
return new NullAwareComparer(this);
}
}
const dummyComparer = new class DummyComparer extends BaseComparer<any> {
public override compare(_a: any, _b: any): number {
return 0;
}
public override then(comparer: Comparer<any>): Comparer<any> {
return comparer;
}
public override thenCompare(comparison: Comparison<any>): Comparer<any> {
return createComparer(comparison);
}
public override thenCompareUsing<U>(projection: Converter<any, U>, comparison?: ComparisonOrComparer<U> | undefined): Comparer<any> {
return createComparerUsing(projection, comparison);
}
};
class SimpleComparer<T> extends BaseComparer<T> {
readonly #comparison: Comparison<T>;
public constructor(comparison: Comparison<T>) {
super();
this.#comparison = comparison;
}
public override compare(a: T, b: T) {
return this.#comparison(a, b);
}
public override comparison() {
return this.#comparison;
}
}
class MappedComparer<T, U> extends BaseComparer<T> {
readonly #projection: Converter<T, U>;
readonly #comparison: Comparer<U>;
public constructor(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>) {
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<T> extends BaseComparer<T> {
readonly #base: Comparer<T>;
public constructor(base: Comparer<T>) {
super();
this.#base = base;
}
public override compare(a: T, b: T): number {
return this.#base.compare(b, a);
}
public override reverse(): Comparer<T> {
return this.#base;
}
}
class ThenComparer<T> extends BaseComparer<T> {
readonly #base: Comparer<T>;
readonly #comparer: Comparer<T>;
public constructor(base: Comparer<T>, comparer: Comparer<T>) {
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<any> = new class DefaultComparer extends BaseComparer<any> {
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<T>(): Comparer<T> {
return defaultComparer;
}
class NullAwareComparer<T> extends BaseComparer<Nullable<T>> {
readonly #base: Comparer<T>;
constructor(baseComparer: Comparer<T>) {
super();
this.#base = baseComparer;
}
public override compare(a: Nullable<T>, b: Nullable<T>): 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<Nullable<T>> {
return this;
}
}
export function nullAwareComparer<T>(baseComparer: Comparer<T>): Comparer<Nullable<T>> {
return new NullAwareComparer(baseComparer);
}
export const stringComparer: Comparer<string> = new class StringComparer extends BaseComparer<string> {
public override compare(a: string, b: string): number {
return a.localeCompare(b);
}
};
export const numberComparer: Comparer<number> = new class NumberComparer extends BaseComparer<number> {
public override compare(a: number, b: number): number {
return a - b;
}
};
export const bigintComparer: Comparer<bigint> = new class BigIntComparer extends BaseComparer<bigint> {
public override compare(a: bigint, b: bigint): number {
return Number(a - b);
}
};
export const booleanComparer: Comparer<boolean> = new class BooleanComparer extends BaseComparer<boolean> {
public override compare(a: boolean, b: boolean): number {
return Number(a) - Number(b);
}
};
export const dateComparer: Comparer<Date> = new class DateComparer extends BaseComparer<Date> {
public override compare(a: Date, b: Date): number {
return a.getTime() - b.getTime();
}
};

32
src/comparer/types.ts Normal file
View File

@@ -0,0 +1,32 @@
import { AsyncFunction, Converter, MaybeAsyncConverter, MaybeAsyncFunction } from "../types.js";
import { Nullable } from "../utils.js";
export interface Comparer<T> {
compare(a: T, b: T): number;
comparison(): Comparison<T>;
reverse(): Comparer<T>;
then(comparer: Comparer<T>): Comparer<T>;
thenCompare(comparison: Comparison<T>): Comparer<T>;
thenCompareUsing<U>(projection: Converter<T, U>, comparison?: ComparisonOrComparer<U>): Comparer<T>;
toAsync(): AsyncComparer<T>;
nullAwareComparer(): Comparer<Nullable<T>>;
}
export interface AsyncComparer<T> {
compare(a: T, b: T): Promise<number>;
comparison(): AsyncComparison<T>;
reverse(): AsyncComparer<T>;
then(comparer: AsyncComparer<T>): AsyncComparer<T>;
thenCompare(comparison: AsyncComparison<T>): AsyncComparer<T>;
thenCompareUsing<U>(projection: MaybeAsyncConverter<T, U>, comparison?: AsyncComparisonOrComparer<U>): AsyncComparer<T>;
nullAwareComparer(): AsyncComparer<Nullable<T>>;
}
export type Comparison<T> = (first: T, second: T) => number;
export type ComparisonOrComparer<T> = Comparison<T> | Comparer<T>;
export type AsyncComparison<T> = AsyncFunction<Comparison<T>>;
export type MaybeAsyncComparison<T> = MaybeAsyncFunction<Comparison<T>>;
export type AsyncComparisonOrComparer<T> = AsyncComparison<T> | AsyncComparer<T>;
export type MaybeAsyncComparisonOrComparer<T> = MaybeAsyncComparison<T> | Comparer<T> | AsyncComparer<T>;

View File

@@ -0,0 +1,217 @@
import { MaybeAsyncConverter } from "../types.js";
import { Nullable } from "../utils.js";
import { AsyncEqualityComparer, AsyncEqualityComparison, EqualityComparer, MaybeAsyncEqualityComparison, MaybeAsyncEqualityComparisonOrComparer } from "./types.js";
export function isAsyncEqualityComparer<T>(obj: any): obj is AsyncEqualityComparer<T> {
return obj instanceof BaseAsyncEqualityComparer;
}
export function asAsyncEqualityComparer<T>(equalityComparer: MaybeAsyncEqualityComparisonOrComparer<T>): AsyncEqualityComparer<T> {
return typeof equalityComparer === "function" ? createAsyncEqualityComparer(equalityComparer) : isAsyncEqualityComparer<T>(equalityComparer) ? equalityComparer : fromSyncEqualityComparer(equalityComparer);
}
export function fromSyncEqualityComparer<T>(equalityComparer: EqualityComparer<T>): AsyncEqualityComparer<T> {
return new WrappedAsyncEqualityComparer(equalityComparer);
}
export function createAsyncEqualityComparer<T = any>(equalityComparison: MaybeAsyncEqualityComparison<T>): AsyncEqualityComparer<T> {
return new SimpleAsyncEqualityComparer(equalityComparison);
}
export function createAsyncEqualityComparerUsing<T = any, U = any>(projection: MaybeAsyncConverter<T, U>, equalityComparison?: MaybeAsyncEqualityComparisonOrComparer<U>): AsyncEqualityComparer<T> {
return new MappedAsyncEqualityComparer(projection, equalityComparison);
}
export function combineAsyncEqualityComparers<T>(equalityComparers: Iterable<Nullable<MaybeAsyncEqualityComparisonOrComparer<T>>>) {
let result: AsyncEqualityComparer<T> = alwaysTrueAsyncEqualityComparer;
for (const equalityComparer of equalityComparers) {
if (!equalityComparer) {
continue;
}
result = result.then(asAsyncEqualityComparer(equalityComparer));
}
return result === alwaysTrueAsyncEqualityComparer ? undefined : result;
}
export abstract class BaseAsyncEqualityComparer<T> implements AsyncEqualityComparer<T> {
#cachedBoundEqualityComparison: AsyncEqualityComparison<T> | undefined;
public abstract equals(a: T, b: T): Promise<boolean>;
public equalityComparison(): AsyncEqualityComparison<T> {
return this.#cachedBoundEqualityComparison ??= this.equals.bind(this);
}
public opposite(): AsyncEqualityComparer<T> {
return new OppositeAsyncEqualityComparer(this);
}
public then(equalityComparer: AsyncEqualityComparer<T>): AsyncEqualityComparer<T> {
return new ThenAsyncEqualityComparer(this, equalityComparer);
}
public thenEquals(equalityComparison: MaybeAsyncEqualityComparison<T>): AsyncEqualityComparer<T> {
return this.then(createAsyncEqualityComparer(equalityComparison));
}
public thenEqualsUsing<U>(projection: MaybeAsyncConverter<T, U>, equalityComparison?: MaybeAsyncEqualityComparisonOrComparer<U>): AsyncEqualityComparer<T> {
return this.then(createAsyncEqualityComparerUsing(projection, equalityComparison));
}
}
class WrappedAsyncEqualityComparer<T> extends BaseAsyncEqualityComparer<T> {
readonly #base: EqualityComparer<T>;
constructor(base: EqualityComparer<T>) {
super();
this.#base = base;
}
public override async equals(a: T, b: T) {
return this.#base.equals(a, b);
}
}
class SimpleAsyncEqualityComparer<T> extends BaseAsyncEqualityComparer<T> {
readonly #equalityComparison: MaybeAsyncEqualityComparison<T>;
public constructor(equalityComparison: MaybeAsyncEqualityComparison<T>) {
super();
this.#equalityComparison = equalityComparison;
}
public override async equals(a: T, b: T): Promise<boolean> {
return await this.#equalityComparison(a, b);
}
}
class MappedAsyncEqualityComparer<T, U> extends BaseAsyncEqualityComparer<T> {
readonly #projection: MaybeAsyncConverter<T, U>;
readonly #equalityComparison: AsyncEqualityComparer<U>;
public constructor(projection: MaybeAsyncConverter<T, U>, equalityComparison?: MaybeAsyncEqualityComparisonOrComparer<U>) {
super();
this.#projection = projection;
this.#equalityComparison = equalityComparison ? asAsyncEqualityComparer(equalityComparison) : defaultAsyncEqualityComparer;
}
public override async equals(a: T, b: T): Promise<boolean> {
return await this.#equalityComparison.equals(await this.#projection(a), await this.#projection(b));
}
}
class OppositeAsyncEqualityComparer<T> extends BaseAsyncEqualityComparer<T> {
readonly #base: AsyncEqualityComparer<T>;
public constructor(base: AsyncEqualityComparer<T>) {
super();
this.#base = base;
}
public override async equals(a: T, b: T): Promise<boolean> {
return !(await this.#base.equals(a, b));
}
public override opposite(): AsyncEqualityComparer<T> {
return this.#base;
}
}
class ThenAsyncEqualityComparer<T> extends BaseAsyncEqualityComparer<T> {
readonly #base: AsyncEqualityComparer<T>;
readonly #equalityComparer: AsyncEqualityComparer<T>;
public constructor(base: AsyncEqualityComparer<T>, equalityComparer: AsyncEqualityComparer<T>) {
super();
this.#base = base;
this.#equalityComparer = equalityComparer;
}
public override async equals(a: T, b: T) {
return await this.#base.equals(a, b) && await this.#equalityComparer.equals(a, b);
}
}
export const alwaysAsyncFalseEqualityComparer = new class AlwaysFalseAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(_a: any, _b: any): Promise<boolean> {
return false;
}
public override opposite(): AsyncEqualityComparer<any> {
return alwaysTrueAsyncEqualityComparer;
}
public override then(_equalityComparer: AsyncEqualityComparer<any>): AsyncEqualityComparer<any> {
return this;
}
public override thenEquals(_equalityComparison: MaybeAsyncEqualityComparison<any>): AsyncEqualityComparer<any> {
return this;
}
public override thenEqualsUsing<U>(_projection: MaybeAsyncConverter<any, U>, _equalityComparison?: MaybeAsyncEqualityComparisonOrComparer<U> | undefined): AsyncEqualityComparer<any> {
return this;
}
};
export const alwaysTrueAsyncEqualityComparer = new class AlwaysTrueAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(_a: any, _b: any): Promise<boolean> {
return true;
}
public override opposite(): AsyncEqualityComparer<any> {
return alwaysAsyncFalseEqualityComparer;
}
public override then(equalityComparer: AsyncEqualityComparer<any>): AsyncEqualityComparer<any> {
return equalityComparer;
}
public override thenEquals(equalityComparison: MaybeAsyncEqualityComparison<any>): AsyncEqualityComparer<any> {
return createAsyncEqualityComparer(equalityComparison);
}
public override thenEqualsUsing<U>(projection: MaybeAsyncConverter<any, U>, equalityComparison?: MaybeAsyncEqualityComparisonOrComparer<U> | undefined): AsyncEqualityComparer<any> {
return createAsyncEqualityComparerUsing(projection, equalityComparison);
}
};
export const looseAsyncEqualityComparer: AsyncEqualityComparer<any> = new class LooseAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(a: any, b: any): Promise<boolean> {
return a == b;
}
};
export const strictAsyncEqualityComparer: AsyncEqualityComparer<any> = new class StrictAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(a: any, b: any): Promise<boolean> {
return a === b;
}
};
export const sameValueAsyncEqualityComparer: AsyncEqualityComparer<any> = new class SameValueAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(a: any, b: any): Promise<boolean> {
return Object.is(a, b);
}
};
export const defaultAsyncEqualityComparer: AsyncEqualityComparer<any> = new class DefaultAsyncEqualityComparer extends BaseAsyncEqualityComparer<any> {
public override async equals(a: any, b: any): Promise<boolean> {
return a === b;
}
};
export function getDefaultAsyncEqualityComparer<T>(): AsyncEqualityComparer<T> {
return defaultAsyncEqualityComparer;
}
export const dateAsyncEqualityComparer: AsyncEqualityComparer<Date> = new class DateAsyncEqualityComparer extends BaseAsyncEqualityComparer<Date> {
public override async equals(a: Date, b: Date): Promise<boolean> {
return a.getTime() === b.getTime();
}
};

View File

@@ -0,0 +1,203 @@
import { Converter } from "../types.js";
import { Nullable } from "../utils.js";
import { EqualityComparer, EqualityComparison, EqualityComparisonOrComparer } from "./types.js";
export function isEqualityComparer<T>(obj: any): obj is EqualityComparer<T> {
return obj instanceof BaseEqualityComparer;
}
export function asEqualityComparer<T>(equalityComparer: EqualityComparisonOrComparer<T>): EqualityComparer<T> {
return typeof equalityComparer === "function" ? createEqualityComparer(equalityComparer) : equalityComparer;
}
export function createEqualityComparer<T = any>(equalityComparison: EqualityComparison<T>): EqualityComparer<T> {
return new SimpleEqualityComparer(equalityComparison);
}
export function createEqualityComparerUsing<T = any, U = any>(projection: Converter<T, U>, equalityComparison?: EqualityComparisonOrComparer<U>): EqualityComparer<T> {
return new MappedEqualityComparer(projection, equalityComparison);
}
export function combineEqualityComparers<T>(equalityComparers: Iterable<Nullable<EqualityComparisonOrComparer<T>>>) {
let result: EqualityComparer<T> = alwaysTrueEqualityComparer;
for (const equalityComparer of equalityComparers) {
if (!equalityComparer) {
continue;
}
result = result.then(asEqualityComparer(equalityComparer));
}
return result === alwaysTrueEqualityComparer ? undefined : result;
}
export abstract class BaseEqualityComparer<T> implements EqualityComparer<T> {
#cachedBoundEqualityComparison: EqualityComparison<T> | undefined;
public abstract equals(a: T, b: T): boolean;
public equalityComparison(): EqualityComparison<T> {
return this.#cachedBoundEqualityComparison ??= this.equals.bind(this);
}
public opposite(): EqualityComparer<T> {
return new OppositeEqualityComparer(this);
}
public then(equalityComparer: EqualityComparer<T>): EqualityComparer<T> {
return new ThenEqualityComparer(this, equalityComparer);
}
public thenEquals(equalityComparison: EqualityComparison<T>): EqualityComparer<T> {
return this.then(createEqualityComparer(equalityComparison));
}
public thenEqualsUsing<U>(projection: Converter<T, U>, equalityComparison?: EqualityComparisonOrComparer<U>): EqualityComparer<T> {
return this.then(createEqualityComparerUsing(projection, equalityComparison));
}
}
class SimpleEqualityComparer<T> extends BaseEqualityComparer<T> {
readonly #equalityComparison: EqualityComparison<T>;
public constructor(equalityComparison: EqualityComparison<T>) {
super();
this.#equalityComparison = equalityComparison;
}
public override equals(a: T, b: T): boolean {
return this.#equalityComparison(a, b);
}
public override equalityComparison() {
return this.#equalityComparison;
}
}
class MappedEqualityComparer<T, U> extends BaseEqualityComparer<T> {
readonly #projection: Converter<T, U>;
readonly #equalityComparison: EqualityComparer<U>;
public constructor(projection: Converter<T, U>, equalityComparison?: EqualityComparisonOrComparer<U>) {
super();
this.#projection = projection;
this.#equalityComparison = equalityComparison ? asEqualityComparer(equalityComparison) : defaultEqualityComparer;
}
public override equals(a: T, b: T): boolean {
return this.#equalityComparison.equals(this.#projection(a), this.#projection(b));
}
}
class OppositeEqualityComparer<T> extends BaseEqualityComparer<T> {
readonly #base: EqualityComparer<T>;
public constructor(base: EqualityComparer<T>) {
super();
this.#base = base;
}
public override equals(a: T, b: T): boolean {
return !this.#base.equals(a, b);
}
public override opposite(): EqualityComparer<T> {
return this.#base;
}
}
class ThenEqualityComparer<T> extends BaseEqualityComparer<T> {
readonly #base: EqualityComparer<T>;
readonly #equalityComparer: EqualityComparer<T>;
public constructor(base: EqualityComparer<T>, equalityComparer: EqualityComparer<T>) {
super();
this.#base = base;
this.#equalityComparer = equalityComparer;
}
public override equals(a: T, b: T) {
return this.#base.equals(a, b) && this.#equalityComparer.equals(a, b);
}
}
export const alwaysFalseEqualityComparer = new class AlwaysFalseEqualityComparer extends BaseEqualityComparer<any> {
public override equals(_a: any, _b: any): boolean {
return false;
}
public override opposite(): EqualityComparer<any> {
return alwaysTrueEqualityComparer;
}
public override then(_equalityComparer: EqualityComparer<any>): EqualityComparer<any> {
return this;
}
public override thenEquals(_equalityComparison: EqualityComparison<any>): EqualityComparer<any> {
return this;
}
public override thenEqualsUsing<U>(_projection: Converter<any, U>, _equalityComparison?: EqualityComparisonOrComparer<U> | undefined): EqualityComparer<any> {
return this;
}
};
export const alwaysTrueEqualityComparer = new class AlwaysTrueEqualityComparer extends BaseEqualityComparer<any> {
public override equals(_a: any, _b: any): boolean {
return true;
}
public override opposite(): EqualityComparer<any> {
return alwaysFalseEqualityComparer;
}
public override then(equalityComparer: EqualityComparer<any>): EqualityComparer<any> {
return equalityComparer;
}
public override thenEquals(equalityComparison: EqualityComparison<any>): EqualityComparer<any> {
return createEqualityComparer(equalityComparison);
}
public override thenEqualsUsing<U>(projection: Converter<any, U>, equalityComparison?: EqualityComparisonOrComparer<U> | undefined): EqualityComparer<any> {
return createEqualityComparerUsing(projection, equalityComparison);
}
};
export const looseEqualityComparer: EqualityComparer<any> = new class LooseEqualityComparer extends BaseEqualityComparer<any> {
public override equals(a: any, b: any): boolean {
return a == b;
}
};
export const strictEqualityComparer: EqualityComparer<any> = new class StrictEqualityComparer extends BaseEqualityComparer<any> {
public override equals(a: any, b: any): boolean {
return a === b;
}
};
export const sameValueEqualityComparer: EqualityComparer<any> = new class SameValueEqualityComparer extends BaseEqualityComparer<any> {
public override equals(a: any, b: any): boolean {
return Object.is(a, b);
}
};
export const defaultEqualityComparer: EqualityComparer<any> = new class DefaultEqualityComparer extends BaseEqualityComparer<any> {
public override equals(a: any, b: any): boolean {
return a === b;
}
};
export function getDefaultEqualityComparer<T>(): EqualityComparer<T> {
return defaultEqualityComparer;
}
export const dateEqualityComparer: EqualityComparer<Date> = new class DateEqualityComparer extends BaseEqualityComparer<Date> {
public override equals(a: Date, b: Date): boolean {
return a.getTime() === b.getTime();
}
};

View File

@@ -0,0 +1,28 @@
import { AsyncFunction, Converter, MaybeAsyncConverter, MaybeAsyncFunction } from "../types.js";
export interface EqualityComparer<T> {
equals(a: T, b: T): boolean;
equalityComparison(): EqualityComparison<T>;
opposite(): EqualityComparer<T>;
then(equalityComparer: EqualityComparer<T>): EqualityComparer<T>;
thenEquals(equalityComparison: EqualityComparison<T>): EqualityComparer<T>;
thenEqualsUsing<U>(projection: Converter<T, U>, equalityComparison?: EqualityComparisonOrComparer<U>): EqualityComparer<T>;
}
export interface AsyncEqualityComparer<T> {
equals(a: T, b: T): Promise<boolean>;
equalityComparison(): AsyncEqualityComparison<T>;
opposite(): AsyncEqualityComparer<T>;
then(equalityComparer: AsyncEqualityComparer<T>): AsyncEqualityComparer<T>;
thenEquals(equalityComparison: AsyncEqualityComparison<T>): AsyncEqualityComparer<T>;
thenEqualsUsing<U>(projection: MaybeAsyncConverter<T, U>, equalityComparison?: AsyncEqualityComparisonOrComparer<U>): AsyncEqualityComparer<T>;
}
export type EqualityComparison<T> = (first: T, second: T) => boolean;
export type EqualityComparisonOrComparer<T> = EqualityComparison<T> | EqualityComparer<T>;
export type AsyncEqualityComparison<T> = AsyncFunction<EqualityComparison<T>>;
export type MaybeAsyncEqualityComparison<T> = MaybeAsyncFunction<EqualityComparison<T>>;
export type AsyncEqualityComparisonOrComparer<T> = AsyncEqualityComparison<T> | AsyncEqualityComparer<T>;
export type MaybeAsyncEqualityComparisonOrComparer<T> = MaybeAsyncEqualityComparison<T> | EqualityComparer<T> | AsyncEqualityComparer<T>;

View File

@@ -1,215 +0,0 @@
import { Equater } from "./sync.js";
import { MaybeAsyncEquater } from "./async.js";
import { asAsyncGenerator } from "./utils.js";
export interface EqualityMap<K, V> extends Iterable<[K, V]> {
get(key: K): V | undefined;
set(key: K, value: V): V | undefined;
contains(key: K): boolean;
remove(key: K): V | undefined;
clear(): void;
}
class NativeEqualityMap<K, V> implements EqualityMap<K, V> {
readonly #map = new Map<K, V>();
get(key: K) {
return this.#map.get(key);
}
set(key: K, value: V) {
const existing = this.get(key);
this.#map.set(key, value);
return existing;
}
contains(key: K) {
return this.#map.has(key);
}
remove(key: K) {
const existing = this.get(key);
this.#map.delete(key);
return existing;
}
clear() {
this.#map.clear();
}
[Symbol.iterator]() {
return this.#map[Symbol.iterator]();
}
}
class CustomEqualityMap<K, V> implements EqualityMap<K, V> {
readonly #list: [K, V][] = [];
readonly #keyComparer: Equater<K>;
constructor(keyComparer: Equater<K>) {
this.#keyComparer = keyComparer;
}
get(key: K) {
for (const entry of this.#list) {
if (this.#keyComparer(key, entry[0])) {
return entry[1];
}
}
return undefined;
}
set(key: K, value: V) {
for (const entry of this.#list) {
if (this.#keyComparer(key, entry[0])) {
const previous = entry[1];
entry[1] = value;
return previous;
}
}
this.#list.push([key, value]);
return undefined;
}
contains(key: K) {
for (const entry of this.#list) {
if (this.#keyComparer(key, entry[0])) {
return true;
}
}
return false;
}
remove(key: K) {
for (let i = 0; i < this.#list.length; i++) {
if (this.#keyComparer(key, this.#list[i][0])) {
const removed = this.#list.splice(i, 1);
return removed[0][1];
}
}
return undefined;
}
clear() {
this.#list.length = 0;
}
[Symbol.iterator]() {
return this.#list[Symbol.iterator]();
}
}
export function createEqualityMap<K, V>(keyComparer?: Equater<K>): EqualityMap<K, V> {
return keyComparer ? new CustomEqualityMap<K, V>(keyComparer) : new NativeEqualityMap<K, V>();
}
export interface AsyncEqualityMap<K, V> extends AsyncIterable<[K, V]> {
get(key: K): Promise<V | undefined>;
set(key: K, value: V): Promise<V | undefined>;
contains(key: K): Promise<boolean>;
remove(key: K): Promise<V | undefined>;
clear(): Promise<void>;
}
class NativeAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
readonly #map = new Map<K, V>();
async get(key: K) {
return this.#map.get(key);
}
async set(key: K, value: V) {
const existing = await this.get(key);
this.#map.set(key, value);
return existing;
}
async contains(key: K) {
return this.#map.has(key);
}
async remove(key: K) {
const existing = await this.get(key);
this.#map.delete(key);
return existing;
}
async clear() {
this.#map.clear();
}
[Symbol.asyncIterator]() {
return asAsyncGenerator(this.#map[Symbol.iterator]());
}
}
class CustomAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
readonly #list: [K, V][] = [];
readonly #keyComparer: MaybeAsyncEquater<K>;
constructor(keyComparer: MaybeAsyncEquater<K>) {
this.#keyComparer = keyComparer;
}
async get(key: K) {
for (const entry of this.#list) {
if (await this.#keyComparer(key, entry[0])) {
return entry[1];
}
}
return undefined;
}
async set(key: K, value: V) {
for (const entry of this.#list) {
if (await this.#keyComparer(key, entry[0])) {
const previous = entry[1];
entry[1] = value;
return previous;
}
}
this.#list.push([key, value]);
return undefined;
}
async contains(key: K) {
for (const entry of this.#list) {
if (await this.#keyComparer(key, entry[0])) {
return true;
}
}
return false;
}
async remove(key: K) {
for (let i = 0; i < this.#list.length; i++) {
if (await this.#keyComparer(key, this.#list[i][0])) {
const removed = this.#list.splice(i, 1);
return removed[0][1];
}
}
return undefined;
}
async clear() {
this.#list.length = 0;
}
[Symbol.asyncIterator]() {
return asAsyncGenerator(this.#list[Symbol.iterator]());
}
}
export function createAsyncEqualityMap<K, V>(keyComparer?: MaybeAsyncEquater<K>): AsyncEqualityMap<K, V> {
return keyComparer ? new CustomAsyncEqualityMap<K, V>(keyComparer) : new NativeAsyncEqualityMap<K, V>();
}

293
src/equality-map/impl.ts Normal file
View File

@@ -0,0 +1,293 @@
import { asAsyncEqualityComparer } from "../equality-comparer/async.js";
import { asEqualityComparer } from "../equality-comparer/sync.js";
import { AsyncEqualityComparer, EqualityComparer, EqualityComparisonOrComparer, MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { MaybeAsyncIterable } from "../types.js";
import { AsyncEqualityMap, EqualityMap, EqualityMapEntry } from "./types.js";
export class NativeEqualityMap<K, V> implements EqualityMap<K, V> {
readonly #map = new Map<K, V>();
get size() {
return this.#map.size;
}
get(key: K) {
return this.#map.get(key);
}
set(key: K, value: V) {
const existing = this.get(key);
this.#map.set(key, value);
return existing;
}
setAll(entries: Iterable<EqualityMapEntry<K, V>>) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
contains(key: K) {
return this.#map.has(key);
}
remove(key: K) {
const existing = this.get(key);
this.#map.delete(key);
return existing;
}
clear() {
this.#map.clear();
}
keys() {
return this.#map.keys();
}
values() {
return this.#map.values();
}
entries() {
return this.#map.entries();
}
[Symbol.iterator]() {
return this.#map[Symbol.iterator]();
}
}
export class CustomEqualityMap<K, V> implements EqualityMap<K, V> {
readonly #list: EqualityMapEntry<K, V>[] = [];
readonly #keyComparer: EqualityComparer<K>;
constructor(keyComparer: EqualityComparisonOrComparer<K>) {
this.#keyComparer = asEqualityComparer(keyComparer);
}
get size() {
return this.#list.length;
}
get(key: K) {
for (const entry of this.#list) {
if (this.#keyComparer.equals(key, entry[0])) {
return entry[1];
}
}
return undefined;
}
set(key: K, value: V) {
for (const entry of this.#list) {
if (this.#keyComparer.equals(key, entry[0])) {
const previous = entry[1];
entry[1] = value;
return previous;
}
}
this.#list.push([key, value]);
return undefined;
}
setAll(entries: Iterable<EqualityMapEntry<K, V>>) {
for (const [key, value] of entries) {
this.set(key, value);
}
}
contains(key: K) {
for (const entry of this.#list) {
if (this.#keyComparer.equals(key, entry[0])) {
return true;
}
}
return false;
}
remove(key: K) {
for (let i = 0; i < this.#list.length; i++) {
if (this.#keyComparer.equals(key, this.#list[i][0])) {
const removed = this.#list.splice(i, 1);
return removed[0][1];
}
}
return undefined;
}
clear() {
this.#list.length = 0;
}
*keys() {
for (const entry of this.#list) {
yield entry[0];
}
}
*values() {
for (const entry of this.#list) {
yield entry[1];
}
}
entries() {
return this[Symbol.iterator]();
}
*[Symbol.iterator]() {
for (const entry of this.#list) {
yield entry.slice() as EqualityMapEntry<K, V>; // no entry mutation allowed!
}
}
}
export class NativeAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
readonly #map = new Map<K, V>();
get size() {
return this.#map.size;
}
async get(key: K) {
return this.#map.get(key);
}
async set(key: K, value: V) {
const existing = await this.get(key);
this.#map.set(key, value);
return existing;
}
async setAll(entries: MaybeAsyncIterable<EqualityMapEntry<K, V>>) {
for await (const [key, value] of entries) {
await this.set(key, value);
}
}
async contains(key: K) {
return this.#map.has(key);
}
async remove(key: K) {
const existing = await this.get(key);
this.#map.delete(key);
return existing;
}
clear() {
this.#map.clear();
}
keys() {
return this.#map.keys();
}
values() {
return this.#map.values();
}
entries() {
return this.#map.entries();
}
[Symbol.iterator]() {
return this.#map[Symbol.iterator]();
}
}
export class CustomAsyncEqualityMap<K, V> implements AsyncEqualityMap<K, V> {
readonly #list: EqualityMapEntry<K, V>[] = [];
readonly #keyComparer: AsyncEqualityComparer<K>;
constructor(keyComparer: MaybeAsyncEqualityComparisonOrComparer<K>) {
this.#keyComparer = asAsyncEqualityComparer(keyComparer);
}
get size() {
return this.#list.length;
}
async get(key: K) {
for (const entry of this.#list) {
if (await this.#keyComparer.equals(key, entry[0])) {
return entry[1];
}
}
return undefined;
}
async set(key: K, value: V) {
for (const entry of this.#list) {
if (await this.#keyComparer.equals(key, entry[0])) {
const previous = entry[1];
entry[1] = value;
return previous;
}
}
this.#list.push([key, value]);
return undefined;
}
async setAll(entries: MaybeAsyncIterable<EqualityMapEntry<K, V>>) {
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.equals(key, entry[0])) {
return true;
}
}
return false;
}
async remove(key: K) {
for (let i = 0; i < this.#list.length; i++) {
if (await this.#keyComparer.equals(key, this.#list[i][0])) {
const removed = this.#list.splice(i, 1);
return removed[0][1];
}
}
return undefined;
}
clear() {
this.#list.length = 0;
}
*keys() {
for (const entry of this.#list) {
yield entry[0];
}
}
*values() {
for (const entry of this.#list) {
yield entry[1];
}
}
entries() {
return this[Symbol.iterator]();
}
*[Symbol.iterator]() {
for (const entry of this.#list) {
yield entry.slice() as EqualityMapEntry<K, V>; // no entry mutation allowed!
}
}
}

11
src/equality-map/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import { EqualityComparisonOrComparer, MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { CustomAsyncEqualityMap, CustomEqualityMap, NativeAsyncEqualityMap, NativeEqualityMap } from "./impl.js";
import { AsyncEqualityMap, EqualityMap } from "./types.js";
export function createEqualityMap<K, V>(keyComparer?: EqualityComparisonOrComparer<K>): EqualityMap<K, V> {
return keyComparer ? new CustomEqualityMap<K, V>(keyComparer) : new NativeEqualityMap<K, V>();
}
export function createAsyncEqualityMap<K, V>(keyComparer?: MaybeAsyncEqualityComparisonOrComparer<K>): AsyncEqualityMap<K, V> {
return keyComparer ? new CustomAsyncEqualityMap<K, V>(keyComparer) : new NativeAsyncEqualityMap<K, V>();
}

29
src/equality-map/types.ts Normal file
View File

@@ -0,0 +1,29 @@
import { MaybeAsyncIterable } from "../types.js";
export type EqualityMapEntry<K, V> = [key: K, value: V];
export interface EqualityMap<K, V> extends Iterable<EqualityMapEntry<K, V>> {
readonly size: number;
get(key: K): V | undefined;
set(key: K, value: V): V | undefined;
setAll(entries: Iterable<EqualityMapEntry<K, V>>): void;
contains(key: K): boolean;
remove(key: K): V | undefined;
clear(): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<EqualityMapEntry<K, V>>;
}
export interface AsyncEqualityMap<K, V> extends Iterable<EqualityMapEntry<K, V>> {
readonly size: number;
get(key: K): Promise<V | undefined>;
set(key: K, value: V): Promise<V | undefined>;
setAll(entries: MaybeAsyncIterable<EqualityMapEntry<K, V>>): Promise<void>;
contains(key: K): Promise<boolean>;
remove(key: K): Promise<V | undefined>;
clear(): void;
keys(): IterableIterator<K>;
values(): IterableIterator<V>;
entries(): IterableIterator<EqualityMapEntry<K, V>>;
}

View File

@@ -1,195 +0,0 @@
import { Equater } from "./sync.js";
import { MaybeAsyncEquater } from "./async.js";
import { asAsyncGenerator } from "./utils.js";
export interface EqualitySet<T> extends Iterable<T> {
readonly size: number;
add(obj: T): boolean;
contains(obj: T): boolean;
remove(obj: T): boolean;
clear(): void;
}
class NativeEqualitySet<T> implements EqualitySet<T> {
readonly #set = new Set<T>();
get size() {
return this.#set.size;
}
add(obj: T) {
const exists = this.contains(obj);
this.#set.add(obj);
return !exists;
}
contains(obj: T) {
return this.#set.has(obj);
}
remove(obj: T) {
return this.#set.delete(obj);
}
clear() {
this.#set.clear();
}
[Symbol.iterator]() {
return this.#set[Symbol.iterator]();
}
}
class CustomEqualitySet<T> implements EqualitySet<T> {
readonly #list: T[] = [];
readonly #equater: Equater<T>;
constructor(equater: Equater<T>) {
this.#equater = equater;
}
get size() {
return this.#list.length;
}
add(obj: T) {
if (this.contains(obj)) {
return false;
}
this.#list.push(obj);
return true;
}
contains(obj: T) {
for (const val of this.#list) {
if (this.#equater(obj, val)) {
return true;
}
}
return false;
}
remove(obj: T) {
const length = this.#list.length;
for (let i = 0; i < length; i++) {
if (this.#equater(obj, this.#list[i])) {
this.#list.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this.#list.length = 0;
}
[Symbol.iterator]() {
return this.#list[Symbol.iterator]();
}
}
export function createEqualitySet<T>(equater?: Equater<T>): EqualitySet<T> {
return equater ? new CustomEqualitySet(equater) : new NativeEqualitySet<T>();
}
export interface AsyncEqualitySet<T> extends AsyncIterable<T> {
readonly size: number;
add(obj: T): Promise<boolean>;
contains(obj: T): Promise<boolean>;
remove(obj: T): Promise<boolean>;
clear(): Promise<void>;
}
class NativeAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
readonly #set = new Set<T>();
get size() {
return this.#set.size;
}
async add(obj: T) {
const exists = await this.contains(obj);
this.#set.add(obj);
return !exists;
}
async contains(obj: T) {
return this.#set.has(obj);
}
async remove(obj: T) {
return this.#set.delete(obj);
}
async clear() {
this.#set.clear();
}
[Symbol.asyncIterator]() {
return asAsyncGenerator(this.#set[Symbol.iterator]());
}
}
class CustomAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
readonly #list: T[] = [];
readonly #equater: MaybeAsyncEquater<T>;
constructor(equater: MaybeAsyncEquater<T>) {
this.#equater = equater;
}
get size() {
return this.#list.length;
}
async add(obj: T) {
if (await this.contains(obj)) {
return false;
}
this.#list.push(obj);
return true;
}
async contains(obj: T) {
for (const val of this.#list) {
if (await this.#equater(obj, val)) {
return true;
}
}
return false;
}
async remove(obj: T) {
const length = this.#list.length;
for (let i = 0; i < length; i++) {
if (await this.#equater(obj, this.#list[i])) {
this.#list.splice(i, 1);
return true;
}
}
return false;
}
async clear() {
this.#list.length = 0;
}
[Symbol.asyncIterator]() {
return asAsyncGenerator(this.#list[Symbol.iterator]());
}
}
export function createAsyncEqualitySet<T>(equater?: MaybeAsyncEquater<T>): AsyncEqualitySet<T> {
return equater ? new CustomAsyncEqualitySet(equater) : new NativeAsyncEqualitySet<T>();
}

237
src/equality-set/impl.ts Normal file
View File

@@ -0,0 +1,237 @@
import { asAsyncEqualityComparer } from "../equality-comparer/async.js";
import { asEqualityComparer } from "../equality-comparer/sync.js";
import { AsyncEqualityComparer, EqualityComparer, EqualityComparisonOrComparer, MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { MaybeAsyncIterable } from "../types.js";
import { AsyncEqualitySet, EqualitySet } from "./types.js";
export class NativeEqualitySet<T> implements EqualitySet<T> {
readonly #set = new Set<T>();
get size() {
return this.#set.size;
}
add(value: T) {
const exists = this.contains(value);
this.#set.add(value);
return !exists;
}
addAll(values: Iterable<T>) {
let result = 0;
for (const value of values) {
if (this.add(value)) {
result++;
}
}
return result;
}
contains(value: T) {
return this.#set.has(value);
}
remove(value: T) {
return this.#set.delete(value);
}
clear() {
this.#set.clear();
}
values() {
return this.#set.values();
}
[Symbol.iterator]() {
return this.#set[Symbol.iterator]();
}
}
export class CustomEqualitySet<T> implements EqualitySet<T> {
readonly #list: T[] = [];
readonly #equater: EqualityComparer<T>;
constructor(equater: EqualityComparisonOrComparer<T>) {
this.#equater = asEqualityComparer(equater);
}
get size() {
return this.#list.length;
}
add(value: T) {
if (this.contains(value)) {
return false;
}
this.#list.push(value);
return true;
}
addAll(values: Iterable<T>) {
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.equals(value, val)) {
return true;
}
}
return false;
}
remove(value: T) {
const length = this.#list.length;
for (let i = 0; i < length; i++) {
if (this.#equater.equals(value, this.#list[i])) {
this.#list.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this.#list.length = 0;
}
values() {
return this.#list.values();
}
[Symbol.iterator]() {
return this.#list[Symbol.iterator]();
}
}
export class NativeAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
readonly #set = new Set<T>();
get size() {
return this.#set.size;
}
async add(value: T) {
const exists = await this.contains(value);
this.#set.add(value);
return !exists;
}
async addAll(values: MaybeAsyncIterable<T>) {
let result = 0;
for await (const value of values) {
if (await this.add(value)) {
result++;
}
}
return result;
}
async contains(value: T) {
return this.#set.has(value);
}
async remove(value: T) {
return this.#set.delete(value);
}
clear() {
this.#set.clear();
}
values() {
return this.#set.values();
}
[Symbol.iterator]() {
return this.#set[Symbol.iterator]();
}
}
export class CustomAsyncEqualitySet<T> implements AsyncEqualitySet<T> {
readonly #list: T[] = [];
readonly #equater: AsyncEqualityComparer<T>;
constructor(equater: MaybeAsyncEqualityComparisonOrComparer<T>) {
this.#equater = asAsyncEqualityComparer(equater);
}
get size() {
return this.#list.length;
}
async add(value: T) {
if (await this.contains(value)) {
return false;
}
this.#list.push(value);
return true;
}
async addAll(values: MaybeAsyncIterable<T>) {
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.equals(value, val)) {
return true;
}
}
return false;
}
async remove(value: T) {
const length = this.#list.length;
for (let i = 0; i < length; i++) {
if (await this.#equater.equals(value, this.#list[i])) {
this.#list.splice(i, 1);
return true;
}
}
return false;
}
clear() {
this.#list.length = 0;
}
values() {
return this[Symbol.iterator]();
}
[Symbol.iterator]() {
return this.#list[Symbol.iterator]();
}
}

11
src/equality-set/index.ts Normal file
View File

@@ -0,0 +1,11 @@
import { EqualityComparisonOrComparer, MaybeAsyncEqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { CustomAsyncEqualitySet, CustomEqualitySet, NativeAsyncEqualitySet, NativeEqualitySet } from "./impl.js";
import { AsyncEqualitySet, EqualitySet } from "./types.js";
export function createEqualitySet<T>(equater?: EqualityComparisonOrComparer<T>): EqualitySet<T> {
return equater ? new CustomEqualitySet(equater) : new NativeEqualitySet<T>();
}
export function createAsyncEqualitySet<T>(equater?: MaybeAsyncEqualityComparisonOrComparer<T>): AsyncEqualitySet<T> {
return equater ? new CustomAsyncEqualitySet(equater) : new NativeAsyncEqualitySet<T>();
}

21
src/equality-set/types.ts Normal file
View File

@@ -0,0 +1,21 @@
import { MaybeAsyncIterable } from "../types.js";
export interface EqualitySet<T> extends Iterable<T> {
readonly size: number;
add(value: T): boolean;
addAll(values: Iterable<T>): number;
contains(value: T): boolean;
remove(value: T): boolean;
clear(): void;
values(): IterableIterator<T>;
}
export interface AsyncEqualitySet<T> extends Iterable<T> {
readonly size: number;
add(value: T): Promise<boolean>;
addAll(values: MaybeAsyncIterable<T>): Promise<number>;
contains(value: T): Promise<boolean>;
remove(value: T): Promise<boolean>;
clear(): void;
values(): IterableIterator<T>;
}

View File

@@ -1,4 +1,26 @@
export * from "./sync.js";
export * as async from "./async.js";
export * as collectors from "./collector.js";
export * as random from "./random.js";
export { BaseSequence } from "./sync/impl.js";
export * as Sequences from "./sync/index.js";
export * from "./sync/types.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";
export * from "./collector/types.js";
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 { BaseEqualityComparer } from "./equality-comparer/sync.js";
export * as EqualityComparers from "./equality-comparer/sync.js";
export { BaseAsyncEqualityComparer } from "./equality-comparer/async.js";
export * as AsyncEqualityComparers from "./equality-comparer/async.js";
export * from "./equality-comparer/types.js";
export * as EqualityMaps from "./equality-map/index.js";
export * from "./equality-map/types.js";
export * as EqualitySets from "./equality-set/index.js";
export * from "./equality-set/types.js";

View File

@@ -1,15 +1,6 @@
import { BitArray } from "./bitarray.js";
import { asArray } from "./utils.js";
export type ElementPredicate<T = any> = (index: number, obj: T) => boolean;
export type ElementWeight<T = any> = (index: number, obj: T) => number;
export type RandomGenerator = () => number;
export interface RandomOptions<T = any> {
predicate?: ElementPredicate<T>;
weight?: ElementWeight<T>;
random?: RandomGenerator;
};
import { BitArray } from "../bitarray/index.js";
import { asArray } from "../utils.js";
import { AsyncRandomOptions, ElementPredicate, ElementWeight, RandomElement, RandomGenerator, RandomOptions } from "./types.js";
export const alwaysTrue: ElementPredicate = () => true;
export const weightOfOne: ElementWeight = () => 1.0;
@@ -40,8 +31,10 @@ function mergeOptions<T>(first: RandomOptions<T> | undefined, second: RandomOpti
};
}
function withDefaultOptions<T>(options: RandomOptions<T> | undefined): Required<RandomOptions<T>> {
if (!options) {
function withDefaultOptions<T>(options: RandomOptions<T> | undefined): Required<RandomOptions<T>>;
function withDefaultOptions<T>(options: AsyncRandomOptions<T> | undefined): Required<AsyncRandomOptions<T>>;
function withDefaultOptions<T>(options: RandomOptions<T> | AsyncRandomOptions<T> | undefined): Required<RandomOptions<T> | AsyncRandomOptions<T>> {
if (!options || options === defaultOptions) {
return defaultOptions;
}
@@ -52,9 +45,10 @@ function withDefaultOptions<T>(options: RandomOptions<T> | undefined): Required<
};
}
export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
const { predicate, weight, random } = withDefaultOptions(options);
function _getRandomElement<T>(sequence: Iterable<T>, options: Required<RandomOptions<T>>): RandomElement<T> {
const { predicate, weight, random } = options;
let found = false;
let result: T | undefined = undefined;
let resultIndex = -1;
let index = 0;
@@ -73,34 +67,76 @@ export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptio
weightAcc += w;
if (random() * weightAcc < w) {
found = true;
result = element;
resultIndex = currentIndex;
}
}
}
return { element: result, index: resultIndex };
return { found, element: result, index: resultIndex } as RandomElement<T>;
}
export function getRandomElement<T>(sequence: Iterable<T>, options?: RandomOptions<T>) {
return _getRandomElement(sequence, withDefaultOptions(options));
}
async function _getRandomElementAsync<T>(sequence: AsyncIterable<T>, options: Required<AsyncRandomOptions<T>>): Promise<RandomElement<T>> {
const { predicate, weight, random } = options;
let found = false;
let result: T | undefined = undefined;
let resultIndex = -1;
let index = 0;
let weightAcc = 0.0;
for await (const element of sequence) {
const currentIndex = index++;
if (await predicate(currentIndex, element)) {
const w = await weight(currentIndex, element);
if (w <= 0.0) {
continue;
}
weightAcc += w;
if (random() * weightAcc < w) {
found = true;
result = element;
resultIndex = currentIndex;
}
}
}
return { found, element: result, index: resultIndex } as RandomElement<T>;
}
export async function getRandomElementAsync<T>(sequence: AsyncIterable<T>, options?: AsyncRandomOptions<T>) {
return await _getRandomElementAsync(sequence, withDefaultOptions(options));
}
export class RandomPicker<T> {
readonly #elements: Iterable<T>;
readonly #flags: BitArray;
readonly #options: RandomOptions<T>;
readonly #options: Required<RandomOptions<T>>;
public constructor(elements: T[], options?: RandomOptions<T>) {
public constructor(elements: Iterable<T>, length: number, options?: RandomOptions<T>) {
this.#elements = elements;
this.#flags = new BitArray(elements.length);
this.#flags = BitArray.create(length);
this.#options = withDefaultOptions(mergeOptions({ predicate: i => this.#flags.get(i) }, options));
this.reset();
}
public static from<T>(iterable: Iterable<T>, options?: RandomOptions<T>) {
return new this<T>(asArray(iterable), options);
const arr = asArray(iterable);
return new this<T>(arr, arr.length, options);
}
public static of<T>(options?: RandomOptions<T>, ...values: T[]) {
return new this<T>(values, options);
return new this<T>(values, values.length, options);
}
public get state() {
@@ -112,7 +148,7 @@ export class RandomPicker<T> {
}
public next() {
const result = getRandomElement(this.#elements, this.#options);
const result = _getRandomElement(this.#elements, this.#options);
if (result.index < 0) {
this.reset();

32
src/random/types.ts Normal file
View File

@@ -0,0 +1,32 @@
import { MaybeAsyncFunction } from "../types.js";
export type ElementPredicate<T = any> = (index: number, obj: T) => boolean;
export type ElementWeight<T = any> = (index: number, obj: T) => number;
export type RandomGenerator = () => number;
export type RandomOptions<T = any> = {
predicate?: ElementPredicate<T>;
weight?: ElementWeight<T>;
random?: RandomGenerator;
};
export type MaybeAsyncElementPredicate<T = any> = MaybeAsyncFunction<ElementPredicate<T>>;
export type MaybeAsyncElementWeight<T = any> = MaybeAsyncFunction<ElementWeight<T>>;
export type AsyncRandomOptions<T = any> = {
predicate?: MaybeAsyncElementPredicate<T>;
weight?: MaybeAsyncElementWeight<T>;
random?: RandomGenerator;
};
type RandomElementFound<T> = {
found: true;
element: T;
index: number;
};
type RandomElementNotFound = {
found: false;
element: undefined;
index: -1;
};
export type RandomElement<T> = RandomElementFound<T> | RandomElementNotFound;

View File

@@ -1,8 +1,8 @@
import { MaybeAsyncComparer } from "./async.js";
import { reverseAsyncComparer } from "./utils.js";
import { asAsyncComparer } from "./comparer/async.js";
import { MaybeAsyncComparisonOrComparer, AsyncComparer } from "./comparer/types.js";
export interface AsyncSorter {
sort<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparer<T>): Promise<void>;
sort<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparisonOrComparer<T>): Promise<void>;
}
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<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparer<T>) {
await this._sort(array, descending ? reverseAsyncComparer(comparer) : comparer);
async sort<T>(array: T[], descending: boolean, comparer: MaybeAsyncComparisonOrComparer<T>) {
comparer = asAsyncComparer(comparer);
if (descending) {
comparer = comparer.reverse();
}
await this._sort(array, comparer);
}
protected abstract _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>): Promise<void>;
protected abstract _sort<T>(array: T[], comparer: AsyncComparer<T>): Promise<void>;
}
class InsertionSorter extends BaseAsyncSorter {
protected override async _sort<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
export const insertionSorter: AsyncSorter = new class InsertionSorter extends BaseAsyncSorter {
protected override async _sort<T>(array: T[], comparer: AsyncComparer<T>) {
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<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
export const selectionSorter: AsyncSorter = new class SelectionSorter extends BaseAsyncSorter {
protected async _sort<T>(array: T[], comparer: AsyncComparer<T>) {
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<T>(array: T[], comparer: MaybeAsyncComparer<T>) {
export const bubbleSorter: AsyncSorter = new class BubbleSorter extends BaseAsyncSorter {
protected async _sort<T>(array: T[], comparer: AsyncComparer<T>) {
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();
};

File diff suppressed because it is too large Load Diff

2721
src/sync/impl.ts Normal file

File diff suppressed because it is too large Load Diff

153
src/sync/index.ts Normal file
View File

@@ -0,0 +1,153 @@
import { mathRandom } from "../random/index.js";
import { RandomGenerator } from "../random/types.js";
import { BigIntRangeSequence, ConcatSequence, SequenceMarker, FunctionSequence, GeneratorSequence, RangeSequence, RepeatSequence, RepeatForeverSequence, WrappedArrayLike, WrappedIterable, WrappedMap, WrappedObject, WrappedSet, WrappedArray, EMPTY, ZippedSequence } from "./impl.js";
import { Sequence } from "./types.js";
export function wrap<T = any>(iterable: Iterable<T>): Sequence<T> {
if (isSequence<T>(iterable)) {
return iterable;
}
if (Array.isArray(iterable)) {
return array<T>(iterable);
}
if (iterable instanceof Set) {
return set<T>(iterable);
}
if (iterable instanceof Map) {
return map(iterable) as unknown as Sequence<T>;
}
return sequence<T>(iterable);
}
export function sequence<T = any>(iterable: Iterable<T>): Sequence<T> {
return new WrappedIterable(iterable);
}
export function empty<T = any>(): Sequence<T> {
return EMPTY;
}
export function single<T>(obj: T): Sequence<T> {
return new WrappedObject(obj);
}
export function array<T = any>(array: ReadonlyArray<T>): Sequence<T> {
return new WrappedArray(array);
}
export function arrayLike<T = any>(arrayLike: ArrayLike<T>): Sequence<T> {
return new WrappedArrayLike(arrayLike);
}
export function set<T>(set: ReadonlySet<T>): Sequence<T> {
return new WrappedSet(set);
}
export function map<K, V>(map: ReadonlyMap<K, V>): Sequence<[K, V]> {
return new WrappedMap(map);
}
export function of<T>(...elements: T[]): Sequence<T> {
switch (elements.length) {
case 0:
return empty();
case 1:
return single(elements[0]);
default:
return array(elements);
}
}
export function entries<T>(o: Record<string, T> | ArrayLike<T>): Sequence<[string, T]> {
return array(Object.entries(o));
}
export function keys(o: object): Sequence<string> {
return array(Object.keys(o));
}
export function values(o: object): Sequence<any>;
export function values<T>(o: Record<any, T> | ArrayLike<T>): Sequence<T>;
export function values(o: object) {
return array(Object.values(o));
}
export function func<T>(f: () => T): Sequence<T> {
return new FunctionSequence(f);
}
export function generator<T>(generator: () => Iterable<T>): Sequence<T> {
return new GeneratorSequence(generator);
}
export function range(max: number): Sequence<number>;
export function range(min: number, max: number): Sequence<number>;
export function range(min: number, max: number, step: number): Sequence<number>;
export function range(a: number, b?: number, c?: number): Sequence<number> {
if (b === undefined) {
b = a;
a = 0;
}
if (c === undefined) {
c = 1;
}
return new RangeSequence(a, b, c);
}
export function bigintRange(max: bigint): Sequence<bigint>;
export function bigintRange(min: bigint, max: bigint): Sequence<bigint>;
export function bigintRange(min: bigint, max: bigint, step: bigint): Sequence<bigint>;
export function bigintRange(a: bigint, b?: bigint, c?: bigint): Sequence<bigint> {
if (b === undefined) {
b = a;
a = 0n;
}
if (c === undefined) {
c = 1n;
}
return new BigIntRangeSequence(a, b, c);
}
export function repeat<T>(value: T, count?: number): Sequence<T> {
if (count == undefined) {
return new RepeatForeverSequence(value);
}
if (count < 0) {
throw new RangeError();
}
if (count === 0) {
return empty();
}
if (count === 1) {
return new WrappedObject(value);
}
return new RepeatSequence(value, count);
}
export function randomSequence(random?: RandomGenerator): Sequence<number> {
return new FunctionSequence(random ?? mathRandom);
}
export function concat<T>(...sequences: Iterable<T>[]): Sequence<T> {
return new ConcatSequence(sequences.map(wrap));
}
export function zip<T>(first: Sequence<T>, second: Sequence<T>): Sequence<[T, T]> {
return new ZippedSequence(first, second);
}
export function isSequence<T = any>(obj: any): obj is Sequence<T> {
return obj instanceof SequenceMarker;
}

156
src/sync/types.ts Normal file
View File

@@ -0,0 +1,156 @@
import { AsyncSequence } from "../async/types.js";
import { Collector } from "../collector/types.js";
import { ComparisonOrComparer, Comparer } from "../comparer/types.js";
import { EqualityComparisonOrComparer } from "../equality-comparer/types.js";
import { RandomOptions } from "../random/types.js";
import { AnyPredicate, Converter, TypePredicate, BiConverter, Accumulator, Action } from "../types.js";
export type SequencePipeline<TElement, TResult> = (sequence: Sequence<TElement>) => TResult;
export type SequenceElement<TSequence extends Sequence<any>> = TSequence extends Sequence<infer TElement> ? TElement : never;
export interface Sequence<TElement> extends Iterable<TElement> {
iterator(): Iterator<TElement>;
toJSON(): any;
apply<TResult>(pipeline: SequencePipeline<TElement, TResult>): TResult;
count(predicate?: AnyPredicate<TElement>): number;
nonEnumeratedCount(): number;
fastCount(): number;
maxCount(): number;
select<TResult>(selector: Converter<TElement, TResult>): Sequence<TResult>;
selectMany<TResult>(selector: Converter<TElement, Iterable<TResult>>): Sequence<TResult>;
where<TFiltered extends TElement>(predicate: TypePredicate<TElement, TFiltered>): Sequence<TFiltered>;
where(predicate: AnyPredicate<TElement>): Sequence<TElement>;
groupBy<TKey>(keySelector: Converter<TElement, TKey>, elementSelector?: undefined, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<GroupedSequence<TKey, TElement>>;
groupBy<TKey, TResult>(keySelector: Converter<TElement, TKey>, elementSelector: Converter<TElement, TResult>, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<GroupedSequence<TKey, TResult>>;
join<TOther, TKey>(sequence: Iterable<TOther>, firstKeySelector: Converter<TElement, TKey>, secondKeySelector: Converter<TOther, TKey>, resultSelector?: undefined, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<[TElement, TOther]>;
join<TOther, TKey, TResult>(sequence: Iterable<TOther>, firstKeySelector: Converter<TElement, TKey>, secondKeySelector: Converter<TOther, TKey>, resultSelector: BiConverter<TElement, TOther, TResult>, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<TResult>;
groupJoin<TOther, TKey>(sequence: Iterable<TOther>, firstKeySelector: Converter<TElement, TKey>, secondKeySelector: Converter<TOther, TKey>, resultSelector?: undefined, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<GroupedSequence<TElement, TOther>>;
groupJoin<TOther, TKey, TResult>(sequence: Iterable<TOther>, firstKeySelector: Converter<TElement, TKey>, secondKeySelector: Converter<TOther, TKey>, resultSelector: BiConverter<TElement, Iterable<TOther>, TResult>, keyComparer?: EqualityComparisonOrComparer<TKey>): Sequence<TResult>;
contains(obj: TElement, equater?: EqualityComparisonOrComparer<TElement>): boolean;
sequenceEquals(sequence: Iterable<TElement>, equater?: EqualityComparisonOrComparer<TElement>): boolean;
append(obj: TElement): Sequence<TElement>;
prepend(obj: TElement): Sequence<TElement>;
remove(obj: TElement, all?: boolean, equater?: EqualityComparisonOrComparer<TElement>): Sequence<TElement>;
concat(...sequences: Iterable<TElement>[]): Sequence<TElement>;
first(predicate?: AnyPredicate<TElement>): TElement;
firstOrDefault(predicate?: AnyPredicate<TElement>, def?: TElement): TElement | undefined;
last(predicate?: AnyPredicate<TElement>): TElement;
lastOrDefault(predicate?: AnyPredicate<TElement>, def?: TElement): TElement | undefined;
single(predicate?: AnyPredicate<TElement>): TElement;
singleOrDefault(predicate?: AnyPredicate<TElement>, def?: TElement): TElement | undefined;
elementAt(index: number): TElement;
elementAtOrDefault(index: number, def?: TElement): TElement | undefined;
aggregate(accumulator: Accumulator<TElement, TElement>): TElement;
aggregate<TAccumulator>(accumulator: Accumulator<TElement, TAccumulator>, seed?: TAccumulator): TAccumulator;
aggregate<TAccumulator, TResult>(accumulator: Accumulator<TElement, TAccumulator>, seed?: TAccumulator, resultSelector?: Converter<TAccumulator, TResult>): TResult;
min(comparer?: ComparisonOrComparer<TElement>): TElement;
minBy<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): TElement;
max(comparer?: ComparisonOrComparer<TElement>): TElement;
maxBy<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): TElement;
bounds(comparer?: ComparisonOrComparer<TElement>): [min: TElement, max: TElement];
boundsBy<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): [min: TElement, max: TElement];
order(comparer?: ComparisonOrComparer<TElement>): OrderedSequence<TElement>;
orderBy<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): OrderedSequence<TElement>;
orderDescending(comparer?: ComparisonOrComparer<TElement>): OrderedSequence<TElement>;
orderByDescending<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): OrderedSequence<TElement>;
partition(equater?: EqualityComparisonOrComparer<TElement>): Sequence<Sequence<TElement>>;
partitionBy<TBy>(selector: Converter<TElement, TBy>, equater?: EqualityComparisonOrComparer<TBy>): Sequence<Sequence<TElement>>;
distinct(equater?: EqualityComparisonOrComparer<TElement>): Sequence<TElement>;
distinctBy<TBy>(selector: Converter<TElement, TBy>, equater?: EqualityComparisonOrComparer<TBy>): Sequence<TElement>;
union(sequence: Iterable<TElement>, equater?: EqualityComparisonOrComparer<TElement>): Sequence<TElement>;
unionBy<TBy>(sequence: Iterable<TElement>, selector: Converter<TElement, TBy>, equater?: EqualityComparisonOrComparer<TBy>): Sequence<TElement>;
except(sequence: Iterable<TElement>, equater?: EqualityComparisonOrComparer<TElement>): Sequence<TElement>;
exceptBy<TBy>(sequence: Iterable<TElement>, selector: Converter<TElement, TBy>, equater?: EqualityComparisonOrComparer<TBy>): Sequence<TElement>;
intersect(sequence: Iterable<TElement>, equater?: EqualityComparisonOrComparer<TElement>): Sequence<TElement>;
intersectBy<TBy>(sequence: Iterable<TElement>, selector: Converter<TElement, TBy>, equater?: EqualityComparisonOrComparer<TBy>): Sequence<TElement>;
all(predicate: AnyPredicate<TElement>): boolean;
any(predicate: AnyPredicate<TElement>): boolean;
any(): boolean;
none(predicate: AnyPredicate<TElement>): boolean;
none(): boolean;
skip(n: number): Sequence<TElement>;
skipLast(n: number): Sequence<TElement>;
skipWhile(condition: AnyPredicate<TElement>): Sequence<TElement>;
take(n: number): Sequence<TElement>;
takeLast(n: number): Sequence<TElement>;
takeWhile(condition: AnyPredicate<TElement>): Sequence<TElement>;
peek(action: Action<TElement>): Sequence<TElement>;
forEach(action: Action<TElement>): void;
zip<TOther>(sequence: Iterable<TOther>): Sequence<[TElement, TOther]>;
cartesianProduct<TOther>(sequence: Iterable<TOther>): Sequence<[TElement, TOther]>;
indexed(): Sequence<[number, TElement]>;
reversed(): Sequence<TElement>;
chunked(size: number): Sequence<Sequence<TElement>>;
chunked(size: number, asArray: true): Sequence<TElement[]>;
chunked<TResult>(size: number, transformer: SequencePipeline<TElement, TResult>): Sequence<TResult>;
random(options?: RandomOptions<TElement>): TElement | undefined;
cached(): Sequence<TElement>;
awaited(): AsyncSequence<Awaited<TElement>>;
asArray(): TElement[];
toArray(): TElement[];
toMap<TKey>(keySelector: Converter<TElement, TKey>): Map<TKey, TElement>;
toMap<TKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Map<TKey, TValue>;
toSet(): Set<TElement>;
toObject<TKey extends PropertyKey>(keySelector: Converter<TElement, TKey>): Record<TKey, TElement>;
toObject<TKey extends PropertyKey, TValue>(keySelector: Converter<TElement, TKey>, valueSelector: Converter<TElement, TValue>): Record<TKey, TValue>;
collect<TResult>(collector: Collector<TElement, any, TResult>): TResult;
}
export interface GroupedSequence<TKey, TElement> extends Sequence<TElement> {
get key(): TKey;
}
export interface OrderedSequence<TElement> extends Sequence<TElement> {
get comparer(): Comparer<TElement>;
thenSelf(comparer?: ComparisonOrComparer<TElement>): OrderedSequence<TElement>;
thenBy<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): OrderedSequence<TElement>;
thenSelfDescending(comparer?: ComparisonOrComparer<TElement>): OrderedSequence<TElement>;
thenByDescending<TBy>(selector: Converter<TElement, TBy>, comparer?: ComparisonOrComparer<TBy>): OrderedSequence<TElement>;
}

31
src/types.ts Normal file
View File

@@ -0,0 +1,31 @@
export type Predicate<T> = (obj: T) => boolean;
export type AnyPredicate<T> = (obj: T) => unknown;
export type TypePredicate<TElement, TType extends TElement> = (obj: TElement) => obj is TType;
export type Converter<TFrom, TTo> = (obj: TFrom) => TTo;
export type BiConverter<TFromFirst, TFromSecond, TTo> = (first: TFromFirst, second: TFromSecond) => TTo;
export type Action<T> = (obj: T) => unknown;
export type Accumulator<TElement, TAccumulator> = (acc: TAccumulator, obj: TElement) => TAccumulator;
export type MaybeAsyncIterable<T> = Iterable<T> | AsyncIterable<T>;
export type MaybeAsyncIterator<T> = Iterator<T> | AsyncIterator<T>;
export type MaybeAsyncGenerator<T> = Generator<T> | AsyncGenerator<T>;
export type AsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => Promise<Awaited<R>> : never;
export type MaybePromise<T> = T | Promise<T>;
export type MaybePromiseLike<T> = T | PromiseLike<T>;
export type MaybeAsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => MaybePromise<R> : never;
export type AsyncPredicate<T> = AsyncFunction<Predicate<T>>;
export type AsyncAnyPredicate<T> = AsyncFunction<AnyPredicate<T>>;
export type AsyncTypePredicate<TElement, TType extends TElement> = AsyncFunction<TypePredicate<TElement, TType>>;
export type MaybeAsyncPredicate<T> = MaybeAsyncFunction<Predicate<T>>;
export type MaybeAsyncAnyPredicate<T> = MaybeAsyncFunction<AnyPredicate<T>>;
export type MaybeAsyncTypePredicate<TElement, TType extends TElement> = MaybeAsyncFunction<TypePredicate<TElement, TType>>;
export type AsyncConverter<TFrom, TTo> = AsyncFunction<Converter<TFrom, TTo>>;
export type MaybeAsyncConverter<TFrom, TTo> = MaybeAsyncFunction<Converter<TFrom, TTo>>;
export type AsyncBiConverter<TFromFirst, TFromSecond, TTo> = AsyncFunction<BiConverter<TFromFirst, TFromSecond, TTo>>;
export type MaybeAsyncBiConverter<TFromFirst, TFromSecond, TTo> = MaybeAsyncFunction<BiConverter<TFromFirst, TFromSecond, TTo>>;
export type AsyncAction<T> = AsyncFunction<Action<T>>;
export type MaybeAsyncAction<T> = MaybeAsyncFunction<Action<T>>;
export type AsyncAccumulator<TElement, TAccumulator> = AsyncFunction<Accumulator<TElement, TAccumulator>>;
export type MaybeAsyncAccumulator<T, U> = MaybeAsyncFunction<Accumulator<T, U>>;

View File

@@ -1,120 +1,65 @@
import { AsyncComparer, MaybeAsyncComparer, MaybeAsyncIterator } from "./async.js";
import { Comparer } from "./sync.js";
export type AsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => Promise<Awaited<R>> : never;
export type MaybePromise<T> = T | Promise<T>;
export type MaybeAsyncFunction<TFunc extends (...args: any) => any> = TFunc extends (...args: infer P) => infer R ? (...args: P) => MaybePromise<R> : never;
export type Nullable<T> = T | null | undefined;
export function isDefined<T = any>(obj: T): obj is NonNullable<T> {
return obj !== undefined && obj !== null;
}
export function isIterable<T = any>(obj: any): obj is Iterable<T> {
return isDefined(obj) && typeof obj[Symbol.iterator] === "function";
}
export function isAsyncIterable<T = any>(obj: any): obj is AsyncIterable<T> {
return isDefined(obj) && typeof obj[Symbol.asyncIterator] === "function";
}
export function identity<T>(obj: T) {
return obj;
}
export function looseEquals<T>(a: T, b: T) {
return a == b;
}
export function strictEquals<T>(a: T, b: T) {
return a === b;
}
export function isEquals<T>(a: T, b: T) {
return Object.is(a, b);
}
export function numberCompare<T extends number | bigint>(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<T>(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<T>(first: Comparer<T>, second: Comparer<T>): Comparer<T> {
return (a, b) => first(a, b) || second(a, b);
}
export function combineAsyncComparers<T>(first: MaybeAsyncComparer<T>, second: MaybeAsyncComparer<T>): AsyncComparer<T> {
return async (a, b) => await first(a, b) || await second(a, b);
}
export function reverseComparer<T>(comparer: Comparer<T>): Comparer<T> {
return (a: T, b: T) => comparer(b, a);
}
export function reverseAsyncComparer<T>(comparer: MaybeAsyncComparer<T>): AsyncComparer<T> {
return async (a: T, b: T) => await comparer(b, a);
}
export function* asGenerator<T>(iterator: Iterator<T>) {
while (true) {
const next = iterator.next();
if (next.done) {
return next.value;
}
yield next.value;
}
}
export async function* asAsyncGenerator<T>(iterator: MaybeAsyncIterator<T>) {
while (true) {
const next = await iterator.next();
if (next.done) {
return next.value;
}
yield next.value;
}
}
export function asArray<T>(iterable: Iterable<T>) {
return Array.isArray(iterable) ? <T[]>iterable : Array.from(iterable);
}
class WrappedIterator<T> implements Iterable<T> {
readonly #iterator: Iterator<T>;
export function identity<T>(obj: T) {
return obj;
}
constructor(iterator: Iterator<T>) {
class WrappedAsyncIterator<T> implements AsyncIterable<T> {
readonly #iterator: AsyncIterator<T>;
constructor(iterator: AsyncIterator<T>) {
this.#iterator = iterator;
}
[Symbol.iterator]() {
[Symbol.asyncIterator]() {
return this.#iterator;
}
}
export function wrapAsIterable<T>(iterator: Iterator<T>): Iterable<T> {
return new WrappedIterator(iterator);
export function asAsyncIterable<T>(iterator: AsyncIterator<T>): AsyncIterable<T> {
return isAsyncIterable<T>(iterator) ? iterator : new WrappedAsyncIterator(iterator);
}
const _emptyIterableIterator = new class EmptyIterableIterator implements IterableIterator<any> {
[Symbol.iterator]() {
return this;
}
next(): IteratorResult<any, any> {
return { done: true, value: undefined };
}
return(_value?: any): IteratorResult<any, any> {
throw new Error("Method not implemented.");
}
throw(_e?: any): IteratorResult<any, any> {
throw new Error("Method not implemented.");
}
};
export function emptyIterableIterator<T>(): IterableIterator<T> {
return _emptyIterableIterator;
}
type FindElementSuccess<T> = {
found: true;
element: T;
};
type FindElementFail = {
found: false;
reason?: number;
};
export type FindElementResult<T> = FindElementSuccess<T> | FindElementFail;

View File

@@ -1,4 +0,0 @@
import { register } from "node:module";
import { pathToFileURL } from "node:url";
register("ts-node/esm", pathToFileURL("./"));

View File

@@ -1,24 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "Node16",
"moduleResolution": "Node16",
"module": "nodenext",
"target": "esnext",
"moduleResolution": "nodenext",
"lib": [
"ESNext"
],
"types": ["node"],
"alwaysStrict": true,
"esModuleInterop": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitOverride": true,
"noErrorTruncation": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"allowUnreachableCode": true,
"strict": true
"strict": true,
"rootDir": "./src"
},
"exclude": [
"node_modules"
],
"ts-node": {
"esm": true,
"experimentalSpecifierResolution": "node"
}
}
"include": [
"./src"
]
}