percentile
Calculate one or many percentiles from an array of numbers, TypedArrays, or objects in a single pass.
Repository Health
Technical Analysis
percentile is a tiny, dependency-free JavaScript library for calculating percentiles from an array of values. It exposes a single function that accepts either one percentile or a list of percentiles and returns the corresponding value (or values) from the input array, computing them all in a single sort-and-index pass rather than resorting the array once per requested percentile.
Beyond plain arrays of numbers, it works directly with TypedArrays and with arrays of complex objects — in the latter case, an optional accessor function tells it which numeric field to sort and rank by, and it hands back the original object rather than just the extracted number. Non-numeric (NaN) values are treated as the smallest values instead of causing errors, and out-of-range or non-numeric percentile arguments raise a descriptive validation error naming the offending value.
What You Get
- A single function, percentile(pOrPs, list, fn), covering both scalar and multi-percentile requests in one call.
- Built-in support for plain arrays, TypedArrays, and arrays of objects via an optional accessor function.
- Descriptive validation errors when a requested percentile is outside the 0-100 range or not a number.
- Bundled TypeScript type declarations (lib/index.d.ts) generated from the JSDoc-annotated source.
Common Use Cases
- Computing p50/p95/p99 latency percentiles from an array of request-duration samples.
- Extracting percentile cutoffs (e.g. 90th percentile score) from a numeric dataset before reporting or charting it.
- Getting back the full object at a given percentile rank using the accessor-function form, not just the numeric value.
- Reducing large benchmark or timing sample sets down to representative percentile values in build or CI scripts.
Under The Hood
Architecture percentile is a single-file, functional module (lib/index.js) with no internal layering: it exposes one exported function, percentile(pOrPs, list, fn), built from four small helper functions (validateInput, getPsValue, and two error-message formatters) that live in the same file. Execution is linear — normalize the requested percentile(s) into an array, validate them, clone-and-sort the input list (optionally via the accessor fn), then map each percentile onto its sorted index via getPsValue. There is no dependency injection, no internal state, and no abstraction boundary to reason about beyond the single function’s control flow; the only thing that would break if the core abstraction changed is call sites relying on the exact sort-then-index semantics (e.g. NaN sorting to the front) documented in the README’s Notes section.
Tech Stack The package ships as plain, dependency-free JavaScript (ES5-style var/function syntax, no ES modules) under lib/index.js, with hand-authored JSDoc type annotations that TypeScript (devDependency only) compiles into a separate lib/index.d.ts via the build:types script — the source itself is not TypeScript. Testing uses ava with nyc for coverage reporting; benchmarking uses benchmark.js via scripts/bench.js and scripts/percentile-baseline.js. Linting and formatting are handled by ESLint and Prettier, release automation goes through pmm and conventional-github-releaser with a pre-commit git hook running the full prepare check (format:check, build:types, lint, test) before every commit, and CI runs via a GitHub Actions build workflow. There’s no runtime dependency at all — the published package is just the compiled JS plus its type declarations.
Code Quality Tests cover simple numeric arrays, negative numbers, TypedArrays, array-of-percentiles requests, an accessor-function form for extracting values from objects, and explicit NaN-handling behavior — solid edge-case coverage for a small numeric utility, though there’s no dedicated test asserting the exact thrown-error messages from validateInput. Error handling is explicit: invalid percentiles produce a thrown Error with a human-readable, per-value message rather than silently returning NaN or undefined. Naming is consistent and each function carries a JSDoc block documenting parameter and return types, which doubles as the source for the generated type declarations, but the implementation itself has no compile-time type safety since the source is plain JS rather than TypeScript. ESLint and Prettier are enforced via a pre-commit hook and CI, giving reasonable consistency despite the lack of static typing.
API Design The public API is a single function with a deliberately flexible signature: calling it with one percentile returns one value, calling it with an array of percentiles computes all of them in one pass over the same sorted array, and an optional accessor argument lets callers extract a numeric value from complex objects while getting the original object back as the result — avoiding a separate map-compute-map-back step callers would otherwise write themselves. That flexibility is the one interesting design choice; the underlying sort-and-index algorithm is a standard, well-known approach to percentile calculation with no novel technique. Getting started requires no configuration — install and call the function — which is good baseline developer experience for a utility this narrow in scope.