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 traverse (Maybe)
Section titled “Safe traversal with 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 traverseResult (Result)
Section titled “Safe error traversal with traverseResult (Result)”Maps elements to Result, returning Ok only if every element succeeded, or the first Err
encountered:
Asynchronous traversal with traverseTask and traverseTaskResult
Section titled “Asynchronous traversal with traverseTask and traverseTaskResult”traverseTaskruns all async tasks in parallel, resolving to aTask<A[]>once all complete.traverseTaskResultruns 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, sequenceResult,
sequenceTask, or sequenceTaskResult directly without mapping:
When to use Arr
Section titled “When to use Arr”Use Arr when:
Section titled “Use Arr when:”- Operating inside pipelines: You are sequencing steps point-free inside
pipeorflowchains and want to avoid noisy data-first method wrappers. - Accessing indices safely: You want to avoid runtime
undefinedcrashes and explicitly capture absence viaMaybe. - Flipping async collections: You need to traverse an array with fallible or asynchronous steps,
mapping
Array<TaskResult<E, A>>toTaskResult<E, A[]>cleanly.
Keep using native array methods when:
Section titled “Keep using native array methods when:”- Writing simple local logic: Inside a single, self-contained function body where structural
pipelining is not utilized, and basic
.map()or.filter()chains are already clear.