Dict — Dictionary Utilities
JavaScript provides two primary ways to associate keys with values: plain objects ({}) and the
built-in Map.
A Map is exceptionally useful: it supports any key type (not just strings and symbols), guarantees
insertion-order iteration, and offers highly optimized membership lookups for large collections.
However, in a functional pipeline, the native Map API introduces notable friction:
- It is mutation-based: Methods like
.set()and.delete()alter the map in place, violating the principles of immutability and creating subtle shared-state bugs. - It is data-first: Methods are located on the prototype, forcing us to write verbose inline
arrow wrappers inside
pipechains. - It is unsafe: The
.get()method silently returnsundefinedwhen a key is absent, shifting the checking burden back to our code.
Dict solves these limitations. It acts as a wrapper around the standard ReadonlyMap<K, V>,
providing a suite of pure, data-last, immutable utilities that return Maybe containers for
safe, explicit lookups.
Creating Dictionaries
Section titled “Creating Dictionaries”We lift key-value associations into the Dict context using its core constructors:
Dict.singleton(key, value) is also available to quickly construct a typed dictionary holding
exactly one initial entry.
Safe Lookups: Bypassing Undefined
Section titled “Safe Lookups: Bypassing Undefined”Instead of returning nullable values, Dict.lookup explicitly yields a Maybe container:
If you only need to verify whether a key is present and do not need to retrieve its value,
Dict.has is the optimized tool, avoiding the allocations of a Maybe instance:
Transforming Values
Section titled “Transforming Values”Dict.map applies a transformation to every value inside the dictionary, returning a new dictionary
with the same keys:
If the transformation depends on the key as well as the value, Dict.mapWithKey passes both to your
callback:
Filtering Values
Section titled “Filtering Values”Dict.filterfilters out entries whose values do not satisfy a predicate.Dict.filterWithKeypasses both the key and the value to the predicate:
The Single-Pass map-and-filter: filterMap
Section titled “The Single-Pass map-and-filter: filterMap”When you need to map dictionary values and filter out empty or invalid items simultaneously,
Dict.filterMap performs both operations in a single pass, collecting only the successful
Some values:
Modifying Entries Immutably
Section titled “Modifying Entries Immutably”Unlike native Map operations, modifying a Dict never alters the original instance. Every
modification returns a fresh, structurally copied dictionary:
Inserting or updating with upsert
Section titled “Inserting or updating with upsert”For the common pattern of incrementing a counter or initializing a default value on first write,
Dict.upsert provides a single, unified operation. It passes Some(value) to your updater function
if the key exists, or None if the key is absent:
Combining Dictionaries
Section titled “Combining Dictionaries”Dict.unionmerges two dictionaries. When a key exists in both, the value from the right-hand dictionary takes precedence (equivalent to spreading objects).Dict.intersectionpreserves only the keys that exist in both dictionaries, keeping the values from the left-hand dictionary.Dict.differenceremoves from the left-hand dictionary any keys present in the right-hand dictionary.
Compacting and Folds
Section titled “Compacting and Folds”compactcollapses a dictionary of optional valuesReadonlyMap<K, Maybe<V>>into a clean dictionary of valuesReadonlyMap<K, V>, discardingNonestates.reducefolds the dictionary values from the left into a single accumulator.toRecordexports a string-keyed dictionary back into a plain JavaScript object.
When to use Dict
Section titled “When to use Dict”Use Dict when:
Section titled “Use Dict when:”- Keys are non-strings: You need to associate values using numbers, objects, or custom symbols as keys.
- Order matters: You require guaranteed insertion-order iteration over key-value entries.
- Lookup safety is desired: You want lookups to explicitly return
Maybecontainers rather than nullable values. - Operating in pipelines: You are transforming, filtering, or merging key-value maps point-free
inside
pipechains.
Keep using plain objects (Rec) when:
Section titled “Keep using plain objects (Rec) when:”- Keys are always strings: You are working directly with standard JSON payloads or API responses.
- You require object transformations: You need structural operations like
pick,omit, ormapKeys(which are strictly designed for plain objects).