Arr — Array Utilities
JavaScript arrays feature an exceptionally rich, built-in set of methods. However, when we build structured pipelines, native array methods introduce two notable friction points:
- They are data-first: Native methods reside directly on the array prototype. To sequence them
inside a
pipeorflow, we must wrap them in noisy inline arrow functions:(items) => items.map(f). - They are unsafe: Native lookup methods (like accessing index
[0]or.find()) silently returnundefinedwhen an element is absent or a search misses, shifting the burden of checking back to our code.
Arr solves both structural limitations. It is a comprehensive collection of data-last, curried
utilities designed to slot directly into pipelines, returning explicit Maybe values the moment a
search could result in absence.
Safe Access: Bypassing Undefined
Section titled “Safe Access: Bypassing Undefined”Accessing indices directly in JavaScript can crash our programs or introduce silent, propagating
undefined bugs. Arr provides safe, explicit boundary boundaries:
Because these returns are standard Maybe containers, they compose linearly without a single
conditional guard:
Searching and Filtering
Section titled “Searching and Filtering”Searches are guaranteed to return safe optional values:
Standard transformation steps are curried and ready for pipe composition:
Partitioning and grouping
Section titled “Partitioning and grouping”partitiondivides a collection into two groups: those that pass a predicate and those that fail.groupBymaps elements into a record of non-empty lists grouped by a key function:
Deduplication and sorting
Section titled “Deduplication and sorting”uniqfilters duplicates using strict equality (===).uniqByfilters duplicates by projecting a key.sortBysorts values immutably without mutating the source array:
FlatMap and Flatten
Section titled “FlatMap and Flatten”For nested collections:
The Map-Filter Superpower: filterMap
Section titled “The Map-Filter Superpower: filterMap”We frequently need to map over a collection and filter out invalid or empty results. Writing this natively requires two complete array iterations:
filterMap performs both mapping and filtering in a single pass, collecting only the successful
Some values and discarding None states automatically:
Index Slicing and Modification
Section titled “Index Slicing and Modification”Safe modifications
Section titled “Safe modifications”Unlike direct mutations or bracket insertions, these return a fresh, structurally copied array, preserving immutability:
insertAtplaces an item at a given index (negative clamp to0, overflow appends).removeAtremoves the element at an index (out of bounds returns the original array unchanged).
Combinations and Folds
Section titled “Combinations and Folds”zippairs elements from two arrays, terminating at the length of the shorter array.zipWithcombines elements using a custom function.intersperseinjects a separator between every element.chunksOfsplits an array into fixed-size chunks.reducefolds a collection from the left.
Traversal across Contexts: traverse and sequence
Section titled “Traversal across Contexts: traverse and sequence”When you map an array using an operation that can fail or runs asynchronously, you end up with an
array of containers, such as Array<Maybe<A>> or Array<Result<E, A>>.
This is highly inconvenient. Typically, we want to flip this structure inside out: if all
operations passed, we want Maybe<Array<A>> or Result<E, Array<A>>. If a single check failed, we
want the entire pipeline to fail.
The traverse family executes this inside-out flip automatically during the mapping stage.
Safe traversal with Arr.traverse.Maybe
Section titled “Safe traversal with Arr.traverse.Maybe”Maps each element to a Maybe and flattens it. If a single element yields None, the entire result
resolves to None:
Safe error traversal with Arr.traverse.Result
Section titled “Safe error traversal with Arr.traverse.Result”Maps elements to Result, returning Ok only if every element succeeded, or the first Err
encountered:
Asynchronous traversal with Arr.traverse.Task and Arr.traverse.Task.Result
Section titled “Asynchronous traversal with Arr.traverse.Task and Arr.traverse.Task.Result”Arr.traverse.Taskruns all async tasks in parallel, resolving to aTask<A[]>once all complete.Arr.traverse.Task.Resultruns tasks sequentially, short-circuiting on the firstErrencountered.
Flipping existing structures: sequence
Section titled “Flipping existing structures: sequence”If you already have an array of containers, you can flip them using sequence directly under the
new layout:
Non-Empty Arrays: Arr.NonEmpty and Generic Operations
Section titled “Non-Empty Arrays: Arr.NonEmpty and Generic Operations”When you need compile-time guarantees that an array is not empty (e.g., for safe head access or
accumulating validation errors), you can use Arr.NonEmpty<A> from the data module.
To simplify operating on non-empty arrays, several core Arr helpers are generic and automatically
preserve the non-empty type contract when applied to a Arr.NonEmpty. These include map,
mapWithIndex, reverse, intersperse, prepend, append, and concat.
Specialized Non-Empty Operations: Arr.NonEmpty
Section titled “Specialized Non-Empty Operations: Arr.NonEmpty”For operations that have structurally distinct signatures or return shapes when applied to non-empty
arrays, you can use the nested Arr.NonEmpty module.
head/last: Because a non-empty array is guaranteed to contain elements, these helpers return the value directly instead of wrapping it in aMaybe.tail: Returns all elements after the first as a standardreadonly A[].reduce: Reduces the array from the left without requiring an initial seed value, since there is always at least one element.singleton: Wraps a single value in anArr.NonEmpty.from.Array: Attempts to lift a standard, potentially empty array into anArr.NonEmpty, returningSome<Arr.NonEmpty>if elements are present, andNoneotherwise.
For more details on when to enforce non-empty guarantees at the boundaries of your systems, see the dedicated NonEmpty Guide.
Problems it solves
Section titled “Problems it solves”- Point-free transformation in data pipelines: In API response formatters and event processors,
transforming arrays with native methods often requires verbose arrow wrapper functions inside
pipechains.Arrprovides data-last combinators (Arr.map,Arr.filterMap,Arr.chunk,Arr.groupBy) that compose cleanly into linear pipelines. - Safe element extraction and out-of-bounds protection: Native indexing (
arr[i]) returnsundefinedat runtime without requiring compile-time handling.Arr.head,Arr.last, andArr.lookupreturnMaybe<A>, ensuring out-of-bounds accesses are safely handled before accessing properties. - Batch chunking for rate-limited APIs (
Arr.chunk): When submitting bulk inserts to a database or making external API calls with payload size limits, large arrays must be partitioned into smaller batches.Arr.chunksplits collections into fixed-size segments point-free. - Categorization and dual-partitioning (
Arr.groupBy,Arr.partition): In UI dashboards and report generators, records often need to be split into active/inactive buckets (Arr.partition) or organized by category keys (Arr.groupBy) for sectioned list rendering. - Simultaneous mapping and filtering (
Arr.filterMap,Arr.compact): Extracting valid data from dirty datasets (such as parsing strings to numbers and discarding unparseable rows) typically requires separate.map()and.filter()passes.Arr.filterMapexecutes transformation and filtering in a single efficient pass. - Traversing collections of fallible or asynchronous steps: When running batch operations (such
as validating an array of input records or fetching details for a list of IDs), standard mapping
produces
Array<Task.Result<E, A>>.Arr.traverseTaskResultsequences or parallels the collection into a singleTask.Result<E, A[]>, handling failures and collection inversion automatically.