diff --git a/.changeset/sixty-parents-travel.md b/.changeset/sixty-parents-travel.md new file mode 100644 index 0000000..539af34 --- /dev/null +++ b/.changeset/sixty-parents-travel.md @@ -0,0 +1,8 @@ +--- +"@oviirup/utils": patch +--- + +✨ added `array.compact` + +- 🗑️ removed unnecessary functions +- ♻️ refactor array.chunk diff --git a/README.md b/README.md index e7e8b41..3b9cf41 100644 --- a/README.md +++ b/README.md @@ -54,14 +54,14 @@ string.toKebabCase("Hello World"); Runtime type checks and a predicate helper. Available from `@oviirup/utils` and `@oviirup/utils/assertions`. -- **`isDefined`** — Non-null and non-undefined -- **`isString`**, **`isNumber`**, **`isInteger`**, **`isFloat`** — Primitive number and string checks -- **`isArray`**, **`isEmptyArray`** — Array checks -- **`isObject`**, **`isEmptyObject`** — Plain object checks -- **`isEmpty`** — `null`, `undefined`, empty string, empty array, or empty object -- **`isFunction`**, **`isRegex`**, **`isTruthy`** — Function, `RegExp`, and truthiness -- **`isBrowser`** — `typeof window !== "undefined"` -- **`not(fn)`** — Negates an assertion function +- **`isDefined`** - Non-null and non-undefined +- **`isString`**, **`isNumber`**, **`isInteger`**, **`isFloat`** - Primitive number and string checks +- **`isArray`**, **`isEmptyArray`** - Array checks +- **`isObject`**, **`isEmptyObject`** - Plain object checks +- **`isEmpty`** - `null`, `undefined`, empty string, empty array, or empty object +- **`isFunction`**, **`isRegex`**, **`isTruthy`** - Function, `RegExp`, and truthiness +- **`isBrowser`** - `typeof window !== "undefined"` +- **`not(fn)`** - Negates an assertion function ```ts import { isDefined, isString, not } from "@oviirup/utils"; @@ -77,14 +77,12 @@ if (isDefined(user)) { `@oviirup/utils/array` -- **`toArray(value)`** — Wraps a non-array value in an array -- **`unique(array, equals?)`** — Unique items, with an optional equality matcher -- **`at(array, index)`** — Item at index; negative indexes count from the end -- **`first(array)`**, **`last(array)`** — First or last item -- **`range(stop)`** / **`range(start, stop, step?)`** — Numeric range -- **`toFiltered(array, predicate)`** — Filter into a cloned array -- **`move(array, from, to)`** — Move an item in place -- **`chunk(array, size)`** — Split into arrays of `size` +- **`toArray(value)`** - Wraps a non-array value in an array +- **`unique(array, equals?)`** - Unique items, with an optional equality matcher +- **`compact(array)`** - Remove all falsy values from an array. +- **`range(stop)`** / **`range(start, stop, step?)`** - Numeric range +- **`move(array, from, to)`** - Move an item in place +- **`chunk(array, size)`** - Split into arrays of `size` ```ts import { range, unique } from "@oviirup/utils/array"; @@ -97,17 +95,17 @@ range(1, 5); // [1, 2, 3, 4] `@oviirup/utils/number` -- **`inRange(value, min, max)`** — Inclusive range check -- **`clamp(value, min, max)`** — Clamp between `min` and `max` -- **`abbreviate(value, precision? | options?)`** — Compact form (`1000` → `"1.0K"`) +- **`inRange(value, min, max)`** - Inclusive range check +- **`clamp(value, min, max)`** - Clamp between `min` and `max` +- **`abbreviate(value, precision? | options?)`** - Compact form (`1000` → `"1.0K"`) ### Object `@oviirup/utils/object` -- **`keyInObject(object, key)`** — Whether `key` exists on the object -- **`pick(object, keys)`** — Keep the given keys -- **`omit(object, keys)`** — Drop the given keys +- **`keyInObject(object, key)`** - Whether `key` exists on the object +- **`pick(object, keys)`** - Keep the given keys +- **`omit(object, keys)`** - Drop the given keys `keys` may be a single key or an array of keys. @@ -115,10 +113,10 @@ range(1, 5); // [1, 2, 3, 4] `@oviirup/utils/promise` -- **`sleep(ms)`** — Resolve after `ms` milliseconds -- **`retry(fn, retries, delay?)`** — Retry an async function -- **`tryCatch(input)`** — `[value, error]` tuple; `error` is `undefined` on success -- **`timeout(value, ms | { ms, error? })`** — Reject if the work does not finish in time +- **`sleep(ms)`** - Resolve after `ms` milliseconds +- **`retry(fn, retries, delay?)`** - Retry an async function +- **`tryCatch(input)`** - `[value, error]` tuple; `error` is `undefined` on success +- **`timeout(value, ms | { ms, error? })`** - Reject if the work does not finish in time ```ts import { tryCatch, timeout } from "@oviirup/utils/promise"; @@ -131,12 +129,12 @@ await timeout(doWork(), { ms: 5_000, error: "Timed out" }); `@oviirup/utils/string` -- **`slash(str)`** — Convert backslashes to forward slashes -- **`truncate(str, length?)`** — Truncate and append `...` (default length `80`) -- **`words(str)`** — Unicode-aware word split -- **`join(str, delimiter)`** — Join words with a delimiter -- **`toCamelCase`**, **`toPascalCase`**, **`toSnakeCase`**, **`toKebabCase`**, **`toSentenceCase`**, **`toTitleCase`** — Case conversion -- **`slugify(str)`** — Transform any string to dash-separated, URL-safe slug +- **`slash(str)`** - Convert backslashes to forward slashes +- **`truncate(str, length?)`** - Truncate and append `...` (default length `80`) +- **`words(str)`** - Unicode-aware word split +- **`join(str, delimiter)`** - Join words with a delimiter +- **`toCamelCase`**, **`toPascalCase`**, **`toSnakeCase`**, **`toKebabCase`**, **`toSentenceCase`**, **`toTitleCase`** - Case conversion +- **`slugify(str)`** - Transform any string to dash-separated, URL-safe slug ### nanoid diff --git a/bun.lock b/bun.lock index f4e43d2..5cbcb86 100644 --- a/bun.lock +++ b/bun.lock @@ -1,11 +1,11 @@ { - "lockfileVersion": 1, + "lockfileVersion": 2, "configVersion": 1, "workspaces": { "": { "name": "@oviirup/utils", "devDependencies": { - "@biomejs/biome": "^2.5.10", + "@biomejs/biome": "^2.5.11", "@changesets/changelog-github": "^1.0.0", "@changesets/cli": "^3.0.1", "@types/bun": "^1.4.0", @@ -16,23 +16,23 @@ }, }, "packages": { - "@biomejs/biome": ["@biomejs/biome@2.5.10", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.10", "@biomejs/cli-darwin-x64": "2.5.10", "@biomejs/cli-linux-arm64": "2.5.10", "@biomejs/cli-linux-arm64-musl": "2.5.10", "@biomejs/cli-linux-x64": "2.5.10", "@biomejs/cli-linux-x64-musl": "2.5.10", "@biomejs/cli-win32-arm64": "2.5.10", "@biomejs/cli-win32-x64": "2.5.10" }, "bin": { "biome": "bin/biome" } }, "sha512-WRKXARA3kTuiV5sxqTpobJ/I0MVd4vk3pOL6wnp5az4LntFIhWTj1RWZq3DI9PCEN3lXcqy7p5aqUHzvq8AXyQ=="], + "@biomejs/biome": ["@biomejs/biome@2.5.11", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.11", "@biomejs/cli-darwin-x64": "2.5.11", "@biomejs/cli-linux-arm64": "2.5.11", "@biomejs/cli-linux-arm64-musl": "2.5.11", "@biomejs/cli-linux-x64": "2.5.11", "@biomejs/cli-linux-x64-musl": "2.5.11", "@biomejs/cli-win32-arm64": "2.5.11", "@biomejs/cli-win32-x64": "2.5.11" }, "bin": { "biome": "bin/biome" } }, "sha512-Tj0dnkLPdW0ASjHfj2D/ZkkvPU2wrFmnE1jWTD2xzV1ycapV1DutbYXk4NDnR3rYTi1ZCbNFD4G2gRMEY65WaA=="], - "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ItCrxKK6SXVT6flYs0qIuBd4AA3TTTl4d66Re6YI2FuGZnN85NmuYNzkiTJUyYw8qBLv69L5zTUB6uyWd++h3Q=="], + "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.11", "", { "os": "darwin", "cpu": "arm64" }, "sha512-6SGZxoKbXvUjMn1t6A98HqWISPnGNbYs0R/Rt2JarmXBSev+lva4QxUMWEBX9lX1Wo1XTJ78uk5xVDtG58SRZg=="], - "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-yLsPU9pAmtChXDu8vhKAzErqe+LeeYuwuUB2FZMkRitsmdodxsYRa9KHrFispsUHzzOu+9HB3nP/TQxyia+Sjw=="], + "@biomejs/cli-darwin-x64": ["@biomejs/cli-darwin-x64@2.5.11", "", { "os": "darwin", "cpu": "x64" }, "sha512-nYkXY7tLBEgnGbYapDKAyKzgt44ZEyG+AKalvTXtCWKYgepI9dw327q+cVgedxm+Udi1ZzHKUyZrIusHi/KQbw=="], - "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-VG8uQW/86a1roLaIFvtIbEigxIdzdJ190oGyg1tV7VYeQtOS+x10sflk7WbuXgw91EtZX5DlIIIej1YqkNLlcg=="], + "@biomejs/cli-linux-arm64": ["@biomejs/cli-linux-arm64@2.5.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-3PVLSTD9RR73rvVPt5G3T1gc+ycggWEGfTD7RvzzbtcDPD27NxgxBbAFfpm7DXJKW6VLHWE1lLMGvFt2Qxjcow=="], - "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-t1QAKZwQJRB4dvgJSgFiQ4BNfNPChg69BNonz854qLVxnjT3UvDzQg9mbkTJRu35ZqU0Rw10A73J8Urgbg2RPw=="], + "@biomejs/cli-linux-arm64-musl": ["@biomejs/cli-linux-arm64-musl@2.5.11", "", { "os": "linux", "cpu": "arm64" }, "sha512-qhyZUMyCbWYFV2bAwRNVvfMVZ+hv7WYl6mossGrxC+uiQQXhvsuWWU8zz6jYX0mChZd9MgQZbm4vozTmG/5iGw=="], - "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-4O6T0eq2heoHZN0a9UX+rWQoxXEBaKf+lRi2hbsGlHneUz9BWXM76nEWMK7Eeq8gzMxR1khQB6BFpAASpeXqGg=="], + "@biomejs/cli-linux-x64": ["@biomejs/cli-linux-x64@2.5.11", "", { "os": "linux", "cpu": "x64" }, "sha512-JOytptlsgM33B2MMFUg8iBrb4IKpbD5JnJrSeYiaFEeAj4vuXx0iQSQZ4qK7sqyMtfjZxxPdNdMZZVL4y/mFyA=="], - "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.10", "", { "os": "linux", "cpu": "x64" }, "sha512-pgDDqp9JybHm2I0KRgzN6i4+lt8xu4iqxUwLzglUMmOmyRTU1AYBGKzh9sNMOtIjah7xoWvKHlLVetvyifzoiQ=="], + "@biomejs/cli-linux-x64-musl": ["@biomejs/cli-linux-x64-musl@2.5.11", "", { "os": "linux", "cpu": "x64" }, "sha512-oRRlrchG5EfrEL/EmtT1qUjSNHk3/5LGeZhQqADBBAJF1b1ET6964xEKe7aGlGARzDfza8H/seEsFJl7S6Ql9w=="], - "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-pxAbxduPO4xq/Cvgaa2lOrs9BB0hEXmmDqfMNP4ZOffGOkUrD1/QGw9UAMpFQpX2P8MqTIIRuQKcmetum4Oa6A=="], + "@biomejs/cli-win32-arm64": ["@biomejs/cli-win32-arm64@2.5.11", "", { "os": "win32", "cpu": "arm64" }, "sha512-e49E6K9hzH/ohJNx8Y26mY8HaV4I4ZViIeoqhKsmoXLKHhQnMeBAVqCgsGf2Wa3lXlS7RkporDXMHHWkzvZzFw=="], - "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.10", "", { "os": "win32", "cpu": "x64" }, "sha512-M+2dgBsl3lXRiTfgPVc2p3anS4Tocojke4rzFLScZ2Y/wmF+36dRb1iHCLiyGqOzQGyTplZH1HnEYviiAqi3nA=="], + "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.5.11", "", { "os": "win32", "cpu": "x64" }, "sha512-QSQr/KjOgXA7OzXJUWS+oguKyAZ3Q0l/lnlDGbu397eKo83atuWUjBPJrsqbKNF6CARGw8XXJLGzpHC8Ryhd4Q=="], "@changesets/apply-release-plan": ["@changesets/apply-release-plan@8.0.0", "", { "dependencies": { "@changesets/config": "^4.0.0", "@changesets/format": "^0.1.1", "@changesets/git": "^4.0.0", "@changesets/should-skip-package": "^1.0.0", "@changesets/types": "^7.0.0", "import-meta-resolve": "^4.2.0", "jsonc-parser": "^3.3.1", "semver": "^7.8.1" } }, "sha512-kUd2pbf1w5/AYmBMb0Tt+rkIPCjFJdT0SZMrkOjJT/WV/QbtmvkyB5jkV0oaNPheavprZk+SfUiozUti7TIL2w=="], @@ -78,41 +78,41 @@ "@manypkg/tools": ["@manypkg/tools@2.1.2", "", { "dependencies": { "jju": "^1.4.0", "tinyglobby": "^0.2.13", "yaml": "^2.9.0" } }, "sha512-6QEf6yqFbETdwGITKq57aYoPfX/3K8XFNwsAlx0C1M7o8cb79sv1M3w+tWuWvIcSbNqrLF7OD7YpZMVVz335hQ=="], - "@oxc-project/types": ["@oxc-project/types@0.146.0", "", {}, "sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA=="], + "@oxc-project/types": ["@oxc-project/types@0.148.0", "", {}, "sha512-Nm4s/jB+4FpFsPhWGEC4h7rzksesmtnMXomo6rCMcg/b8zLQuOziRgkCS1fxDCXOlJB/6Q8oABOZ/OP6RIPj9A=="], "@pnpm/deps.graph-sequencer": ["@pnpm/deps.graph-sequencer@1100.0.1", "", {}, "sha512-pOr5+q1fLYKwFN3LAJuGZEnfXDcQ73zqgDHMtGy+K+uIoUqyY+6MeDCWFwfu+4EFuq76I5EPFofoNAI+Bmmq4A=="], "@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="], - "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.5", "", { "os": "android", "cpu": "arm" }, "sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.7", "", { "os": "android", "cpu": "arm" }, "sha512-EypzgnYCwyVY4NDHKzGmNJT5b+XaQEBniHxsMdeIQLB/tcCzZnhqrzHpZFbX9iaxx+5RiB8caATBtfvZP7zVxQ=="], - "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.5", "", { "os": "android", "cpu": "arm64" }, "sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig=="], + "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.7", "", { "os": "android", "cpu": "arm64" }, "sha512-l17HE9EweWaqJZhuUuNBN/FzM62xw+DECVnJyvMsxn8vJFAGLy5QfLDoYAcronkAN8VxKZHezDpulHDPx95vFw=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.2.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8ED8ELFvHXc6OCETIn4gXObPiaR6bckM/ipXtbzlPVDRMBfEGjCKgO90F9YtfdpDatVx/ZQw7aZ1vUMf/+T3Mw=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.2.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-/WPripjtiAIZ2tWY7ddijORT0Ujg87wxWW/qcoFVCKAWVDPhtY0xr7Dj0M3GyNGz60jGwTElhro/mkF9dT7dDQ=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.2.7", "", { "os": "freebsd", "cpu": "x64" }, "sha512-14DI4NcqpvbICxSnGLx3PmtDaWqRP/KGSGb6C+JLLVPeZRl6dKdHba3pGsqT3vpdTqhEYIPG0MMQ8c0xYqoJxA=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.5", "", { "os": "linux", "cpu": "arm" }, "sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.2.7", "", { "os": "linux", "cpu": "arm" }, "sha512-bxrWIRvHWQvbJwi+VIie/kDJmQxcNE6xxWwZdqF/ExVAigtHkv54WTLQPb+QsZdnFy18fg7JPfWGL0RH6vwIlQ=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-toOY2BChBZyuxU7OYX6Tn389di4IzAqPTycVcci0O7FSfBqzRB3RZn+K5Is6ANf4tmgRd/K1yZTsNTXbkXsnLg=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.2.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-lAIXTH/aiLRLxsTgQvfhjo4K1ydWIp00+V0voOr9beb/9ZmkUFrSIb03dXNFRgMNvkE6oGsF10ioQ6UsI+vS5Q=="], - "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA=="], + "@rolldown/binding-linux-ppc64-gnu": ["@rolldown/binding-linux-ppc64-gnu@1.2.7", "", { "os": "linux", "cpu": "ppc64" }, "sha512-kdnwS28Pkenp/mZMRwjXXXwxQ7pIsm+bF919LUK93BOyhcLsrVKdP2p9fxpiPNPAbNuch8ypQt0pm2P2LYCAGg=="], - "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ=="], + "@rolldown/binding-linux-s390x-gnu": ["@rolldown/binding-linux-s390x-gnu@1.2.7", "", { "os": "linux", "cpu": "s390x" }, "sha512-516OdsyLdr5E65paF3yBF55t8mfm9+gmtCsK3xI7XKXIT7EfRlHhxL8K/NR6Hu8BWSgF5+1w74lTL0+nxcc8Qw=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-r8/z8n7GFaYRln3xmP1Cxy0HH/HLM0uBUPkEuSVEfKGDA89M0FsZRZJRSwe/tJjRx+fpH/gjorfhB8tmEbSFLA=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.5", "", { "os": "linux", "cpu": "x64" }, "sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.2.7", "", { "os": "linux", "cpu": "x64" }, "sha512-pAsE8iiDxUg1xBqdhrTfg45AVDVpirjz00sblEYClGNNcMnDb+e8beQgqIAw6LvauX/APvgxUnwrgun/YYGBhw=="], - "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.5", "", { "os": "none", "cpu": "arm64" }, "sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw=="], + "@rolldown/binding-openharmony-arm64": ["@rolldown/binding-openharmony-arm64@1.2.7", "", { "os": "none", "cpu": "arm64" }, "sha512-lTcIYmmnQQA8Or/2DatS6oSqcdLHvendjS+zLu+FwgToynWMRSmQdpM65fTANJgIS4mjbMOo5KT2lnT9SAb96w=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.2.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-e3Gu3WxbNk/UqQhxqU7YIYO+9ZBvWNz3U+h/qRFosscMFzdRPbXYSaSWgSnklv2fz1TgzBTcti2z35c/7irsHw=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.2.7", "", { "os": "win32", "cpu": "x64" }, "sha512-W/jg5qoRSqjsEv0+dZi4e687mcHqmVuU0P4fK6qS/xjetW2Gmc1W8j//z5nAeNcC8Ttm0hV46IjcYeuVwYhuiw=="], "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.1", "", {}, "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw=="], @@ -254,13 +254,13 @@ "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], - "picomatch": ["picomatch@4.0.5", "", {}, "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A=="], + "picomatch": ["picomatch@4.0.7", "", {}, "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA=="], "quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], - "rolldown": ["rolldown@1.2.5", "", { "dependencies": { "@oxc-project/types": "=0.146.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.5", "@rolldown/binding-android-arm64": "1.2.5", "@rolldown/binding-darwin-arm64": "1.2.5", "@rolldown/binding-darwin-x64": "1.2.5", "@rolldown/binding-freebsd-x64": "1.2.5", "@rolldown/binding-linux-arm-gnueabihf": "1.2.5", "@rolldown/binding-linux-arm64-gnu": "1.2.5", "@rolldown/binding-linux-arm64-musl": "1.2.5", "@rolldown/binding-linux-ppc64-gnu": "1.2.5", "@rolldown/binding-linux-s390x-gnu": "1.2.5", "@rolldown/binding-linux-x64-gnu": "1.2.5", "@rolldown/binding-linux-x64-musl": "1.2.5", "@rolldown/binding-openharmony-arm64": "1.2.5", "@rolldown/binding-win32-arm64-msvc": "1.2.5", "@rolldown/binding-win32-x64-msvc": "1.2.5" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA=="], + "rolldown": ["rolldown@1.2.7", "", { "dependencies": { "@oxc-project/types": "=0.148.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.7", "@rolldown/binding-android-arm64": "1.2.7", "@rolldown/binding-darwin-arm64": "1.2.7", "@rolldown/binding-darwin-x64": "1.2.7", "@rolldown/binding-freebsd-x64": "1.2.7", "@rolldown/binding-linux-arm-gnueabihf": "1.2.7", "@rolldown/binding-linux-arm64-gnu": "1.2.7", "@rolldown/binding-linux-arm64-musl": "1.2.7", "@rolldown/binding-linux-ppc64-gnu": "1.2.7", "@rolldown/binding-linux-s390x-gnu": "1.2.7", "@rolldown/binding-linux-x64-gnu": "1.2.7", "@rolldown/binding-linux-x64-musl": "1.2.7", "@rolldown/binding-openharmony-arm64": "1.2.7", "@rolldown/binding-win32-arm64-msvc": "1.2.7", "@rolldown/binding-win32-x64-msvc": "1.2.7" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-g0EtLvBjTUB7jhyV0S/TCup3v/XSVl45vUIGbOGU4QPiyjTenCe4mKuFvW9fEgYmS2Fo42AUssRmNuMziXdrig=="], "rolldown-plugin-dts": ["rolldown-plugin-dts@0.27.14", "", { "dependencies": { "dts-resolver": "^3.0.0", "get-tsconfig": "5.0.0-beta.5", "obug": "^2.1.4", "yuku-ast": "^0.8.0", "yuku-codegen": "^0.8.0", "yuku-parser": "^0.8.0" }, "peerDependencies": { "@typescript/native-preview": "*", "@volar/typescript": "~2.4.0", "rolldown": "^1.0.0", "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", "vue-tsc": "~3.2.0 || ~3.3.0" }, "optionalPeers": ["@typescript/native-preview", "@volar/typescript", "typescript", "vue-tsc"] }, "sha512-ZvuDDwoIpRK9RPxDXratCpklFO9QZZWndf/sd0VBFb4LEj0jj07UcHK9OCh7V4XiFz2Z89ziyBC2K6tJiDjrbw=="], @@ -293,5 +293,9 @@ "yuku-codegen": ["yuku-codegen@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7" }, "optionalDependencies": { "@yuku-codegen/binding-android-arm64": "0.8.7", "@yuku-codegen/binding-darwin-arm64": "0.8.7", "@yuku-codegen/binding-darwin-x64": "0.8.7", "@yuku-codegen/binding-freebsd-x64": "0.8.7", "@yuku-codegen/binding-linux-arm-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm-musl": "0.8.7", "@yuku-codegen/binding-linux-arm64-gnu": "0.8.7", "@yuku-codegen/binding-linux-arm64-musl": "0.8.7", "@yuku-codegen/binding-linux-x64-gnu": "0.8.7", "@yuku-codegen/binding-linux-x64-musl": "0.8.7", "@yuku-codegen/binding-win32-arm64": "0.8.7", "@yuku-codegen/binding-win32-x64": "0.8.7" } }, "sha512-adwDZSh8oVDzhE6Du9PwVWxcOxeV0e2EVhUuMKWfhSY4wkrDq9eqixlxFF3l/XGUV1E7UFzhpz9393MUumkyNw=="], "yuku-parser": ["yuku-parser@0.8.7", "", { "dependencies": { "@yuku-toolchain/types": "^0.8.7", "yuku-ast": "^0.8.7" }, "optionalDependencies": { "@yuku-parser/binding-android-arm64": "0.8.7", "@yuku-parser/binding-darwin-arm64": "0.8.7", "@yuku-parser/binding-darwin-x64": "0.8.7", "@yuku-parser/binding-freebsd-x64": "0.8.7", "@yuku-parser/binding-linux-arm-gnu": "0.8.7", "@yuku-parser/binding-linux-arm-musl": "0.8.7", "@yuku-parser/binding-linux-arm64-gnu": "0.8.7", "@yuku-parser/binding-linux-arm64-musl": "0.8.7", "@yuku-parser/binding-linux-x64-gnu": "0.8.7", "@yuku-parser/binding-linux-x64-musl": "0.8.7", "@yuku-parser/binding-win32-arm64": "0.8.7", "@yuku-parser/binding-win32-x64": "0.8.7" } }, "sha512-vRD9nwt4L3aYpxNqeSC4WqLv58xrXef0Ong1Mc45CTXTIpvLafx7JO05sczmQZwdLEZvywrLOGdNC5+Rp5N1BQ=="], + + "bun-types/@types/node": ["@types/node@26.4.1", "", { "dependencies": { "undici-types": "~8.3.0" } }, "sha512-k97ENvZWtvA6yqz5/FS6a7duDgOPEeOQOc2iKS/nY6mX6qJUKtLnWzQS+Xj6tXweyj6ZcTAK2Qecetnvi9nCLA=="], + + "bun-types/@types/node/undici-types": ["undici-types@8.3.0", "", {}, "sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ=="], } } diff --git a/package.json b/package.json index 4ddefec..4f56974 100644 --- a/package.json +++ b/package.json @@ -20,14 +20,14 @@ "./package.json": "./package.json" }, "scripts": { - "build": "tsdown src/**/*.ts --dts --export --no-fixed-extension", + "build": "tsdown src/**/*.ts --dts --export --no-fixed-extension --unbundle", "format": "biome format --write", "lint": "biome check", "test": "bun test", "typecheck": "tsc --noEmit" }, "devDependencies": { - "@biomejs/biome": "^2.5.10", + "@biomejs/biome": "^2.5.11", "@changesets/changelog-github": "^1.0.0", "@changesets/cli": "^3.0.1", "@types/bun": "^1.4.0", diff --git a/src/array.ts b/src/array.ts index 7939931..a56513f 100644 --- a/src/array.ts +++ b/src/array.ts @@ -1,4 +1,5 @@ -import { isEmptyArray } from "@/assertions"; +import { isEmptyArray, isInteger, isTruthy } from "./assertions"; +import { Truthy } from "./types"; /** * Converts a given value to represent itself in an array @@ -31,41 +32,6 @@ export function unique(value: T[], equals?: Matcher): T[] { }, []); } -/** - * Get nth item of Array. Negative for backward - * @param array The array to get the item from - * @param index The index of the item to get. - * @category Array - */ -export function at(array: readonly [], index: number): undefined; -export function at(array: readonly T[], index: number): T; -export function at(array: readonly T[] | [], index: number): T | undefined { - const len = array.length; - if (!len) return undefined; - if (index < 0) index += len; - return array[index]; -} - -/** - * Get last item - * @category Array - */ -export function last(array: readonly []): undefined; -export function last(array: readonly T[]): T; -export function last(array: readonly T[]): T | undefined { - return at(array, -1); -} - -/** - * Get first item - * @category Array - */ -export function first(array: readonly []): undefined; -export function first(array: readonly T[]): T; -export function first(array: readonly T[]): T | undefined { - return at(array, 0); -} - /** * Generate a range array of numbers. * @category Array @@ -90,22 +56,6 @@ export function range(...args: [number] | [number, number, number?]): number[] { return array; } -type Predicate = (item: T, index: number, array: T[]) => boolean; - -/** - * Filter an array, returning a new array with matching items - * @param array The array to filter - * @param predicate The predicate function to use to filter the array - * @category Array - */ -export function toFiltered(array: T[], predicate: Predicate): T[] { - const result = structuredClone(array); - for (let i = result.length - 1; i >= 0; i--) { - if (!predicate(result[i], i, result)) result.splice(i, 1); - } - return result; -} - /** * Move an item in an array to a new position * @param array The array to move the item in @@ -133,10 +83,31 @@ export function move(array: T[], from: number, to: number): T[] { * @category Array */ export function chunk(array: T[], size: number): T[][] { - return array.reduce((acc, item, index) => { - const chunkIndex = Math.floor(index / size); - if (!acc[chunkIndex]) acc[chunkIndex] = []; - acc[chunkIndex].push(item); - return acc; - }, []); + if (!isInteger(size) && size <= 0) { + throw new Error("Size must be a positive integer"); + } + const length = Math.ceil(array.length / size); + const result: T[][] = []; + for (let i = 0; i < length; i++) { + const start = i * size; + const end = start + size; + result[i] = array.slice(start, end); + } + return result; +} + +/** + * Remove all falsy values from an array, returning a new array. + * @param array The array to compact + * @returns New compacted array + * @category Array + */ +export function compact(array: readonly T[]): Array> { + const result: Array> = []; + const length = array.length; + for (let i = 0; i < length; i++) { + const item = array[i]; + if (!!item) result.push(item as Truthy); + } + return result; } diff --git a/src/assertions.ts b/src/assertions.ts index 3c41a0a..d5990de 100644 --- a/src/assertions.ts +++ b/src/assertions.ts @@ -1,8 +1,10 @@ -const _isNum = (val: unknown) => typeof val === "number"; -const _isObject = (val: unknown) => !_isArray(val) && typeof val === "object"; +import { Negate, Predicate } from "./types"; + +const _isArray = Array.isArray; const _isNan = Number.isNaN; const _isInt = Number.isInteger; -const _isArray = Array.isArray; +const _isNum = (val: unknown) => typeof val === "number"; +const _isObject = (val: unknown) => !_isArray(val) && typeof val === "object"; export const isDefined = (val: T): val is NonNullable => val != null; export const isString = (val: unknown) => typeof val === "string"; @@ -30,14 +32,6 @@ export const isEmpty = (val: unknown): boolean => { return false; }; -export type Predicate = ((val: unknown) => boolean) | ((val: unknown) => val is unknown); - -export type Negate = T extends (val: unknown) => val is infer U - ? (val: V) => val is Exclude - : T extends (val: unknown) => boolean - ? (val: unknown) => boolean - : never; - -export function not(fn: T): Negate { +export function not>(fn: T): Negate { return ((val: unknown) => !fn(val)) as Negate; } diff --git a/src/types.ts b/src/types.ts index 8ac3dd7..a95c66c 100644 --- a/src/types.ts +++ b/src/types.ts @@ -4,16 +4,22 @@ export type Func = (...args: any[]) => T; export type Awaitable = T | Promise; +export type Truthy = T extends false | "" | 0 | null | undefined ? never : T; +export type Falsy = T extends false | "" | 0 | null | undefined ? T : never; + export type AbbreviationSymbols = Dictionary | string[]; export type AbbreviateOptions = { symbols?: AbbreviationSymbols; precision?: number; }; -export type Predicate = ((val: unknown) => boolean) | ((val: unknown) => val is unknown); +export type BasePredicate = (val: unknown) => boolean; +export type TypePredicate = (val: unknown) => val is T; +export type Predicate = BasePredicate | TypePredicate; -export type NegatePredicate = T extends (val: unknown) => val is infer U - ? (val: V) => val is Exclude - : T extends (val: unknown) => boolean - ? (val: unknown) => boolean - : never; +export type Negate = + T extends TypePredicate + ? (val: V) => val is Exclude + : T extends BasePredicate + ? (val: unknown) => boolean + : never; diff --git a/tests/array.test.ts b/tests/array.test.ts index 551db98..94aac8b 100644 --- a/tests/array.test.ts +++ b/tests/array.test.ts @@ -49,4 +49,15 @@ describe("array", () => { ]); }); }); + + describe("compact", () => { + it("returns empty array for empty input", () => { + expect(array.compact([])).toEqual([]); + }); + it("removes falsy values", () => { + expect(array.compact([0, 1, false, 2, "", 3, null, undefined, NaN])).toEqual([1, 2, 3]); + expect(array.compact([false, 0, -0, 0n, "", null, undefined, NaN])).toEqual([]); + expect(array.compact([0, { name: "John" }, false, NaN, ""])).toEqual([{ name: "John" }]); + }); + }); }); diff --git a/tsconfig.json b/tsconfig.json index 8194e74..e69d7dc 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -11,7 +11,7 @@ "resolveJsonModule": true, "skipLibCheck": true, "strict": true, - "target": "ES2017", + "target": "ES2020", "types": ["bun", "node"] }, "exclude": ["node_modules", "dist"],